I have a form where I'd like the user to add any amount of promo codes. The closest native example of this is in the Contacts app. See add url section, which I wouldn't need the "homepage" or "home" links but the delete functionality is:

How can this be constructed with SwiftUI or is there a more recommended way of handling this scenario? Thanks for any help!
So this is very quick and dirty, but it should give you an idea of a direction to go in:
struct PromoCodeView: View {
@State var codes = [String]()
func getBinding(forIndex index: Int) -> Binding<String> {
return Binding<String>(get: { codes[index] },
set: { codes[index] = $0 })
}
var body: some View {
NavigationView {
Form {
Section(header: Text("Promo Codes")) {
ForEach(0..<codes.count, id: \.self) { index in
HStack {
Button(action: { codes.remove(at: index) }) {
Image(systemName: "minus.circle.fill")
.foregroundColor(.red)
.padding(.horizontal)
}
TextField("Promo Code", text: getBinding(forIndex: index))
}
}
Button(action: { codes.append("") }) {
HStack {
Image(systemName: "plus.circle.fill")
.foregroundColor(.green)
.padding(.horizontal)
Text("add promo code")
}
}
}
}
.navigationBarTitle("Codes")
}
}
}
And looks like this:

Okay, so I've made it a lot more generic so you can just drop the view in and give it a binding to your string array:
struct ListEditor: View {
var title: String
var placeholderText: String
var addText: String
@Binding var list: [String]
func getBinding(forIndex index: Int) -> Binding<String> {
return Binding<String>(get: { list[index] },
set: { list[index] = $0 })
}
var body: some View {
Section(header: Text(title)) {
ForEach(0..<list.count, id: \.self) { index in
ListItem(placeholder: placeholderText, text: getBinding(forIndex: index)) { self.list.remove(at: index) }
}
AddButton(text: addText) { self.list.append("") }
}
}
}
fileprivate struct ListItem: View {
var placeholder: String
@Binding var text: String
var removeAction: () -> Void
var body: some View {
HStack {
Button(action: removeAction) {
Image(systemName: "minus.circle.fill")
.foregroundColor(.red)
.padding(.horizontal)
}
TextField(placeholder, text: $text)
}
}
}
fileprivate struct AddButton: View {
var text: String
var addAction: () -> Void
var body: some View {
Button(action: addAction) {
HStack {
Image(systemName: "plus.circle.fill")
.foregroundColor(.green)
.padding(.horizontal)
Text(text)
}
}
}
}
You can use it like this:
struct TestView: View {
@State var codes = [String]()
var body: some View {
NavigationView {
Form {
ListEditor(title: "Promo Codes",
placeholderText: "Promo Code",
addText: "add promo code",
list: $codes)
}
.navigationBarTitle("Codes")
}
}
}
It looks identical to the above GIF.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With