4

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: enter image description here

How can this be constructed with SwiftUI or is there a more recommended way of handling this scenario? Thanks for any help!

1 Answer 1

11

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:

enter image description here

EDIT:


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.

Sign up to request clarification or add additional context in comments.

1 Comment

Thats how you can add the animation: Button { withAnimation { addAction() } } label: { HStack { Image(systemName: "plus.circle.fill") .foregroundColor(.green) .padding(.horizontal) Text(text) } }

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.