Search code examples
swiftswiftui

SwiftUI - how to append textfield input data into an array?


Here's the text field code:

import SwiftUI
import SwiftUIIntrospect

class TextFieldKeyboardBehavior: UIView, UITextFieldDelegate {
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        return false
    }
}

struct AddIngredientsView: View {
    @State var ingredient = ""
    @State var ingredientsArray: [String] = []

    var textFieldKeyboardBehavior = TextFieldKeyboardBehavior()

    var body: some View {
        VStack {
            if ingredientsArray.count > 0 {
                ScrollView(.vertical, showsIndicators: false) {
                    VStack(spacing: 10) {
                        ForEach(ingredientsArray, id: \.self) { ingredient in
                            Text("\(ingredient)")
                        }
                     }
                 }
             }

             // do something here to append input data in ingredientsArray???
             TextField("Enter an ingredient", text: $ingredient)
                .multilineTextAlignment(.leading)
                .foregroundStyle(Color("SemiDarkGray"))
                .introspect(.textField, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { textField in
                    textField.delegate = textFieldKeyboardBehavior
                 }
             }
         }
     }
}

what i'd like to achieve is that every time a user inputs data in the textfield and then clicks submit in the keyboard, the input data will get appended into an array and be shown on the screen using ForEach or List.

i've tried using onSubmit but Introspect overrides with it which causes onSubmit to not work at all. The reason I'm using Introspect is because I want the keyboard to be visible even after the user clicks submit on the keyboard.

i've also tried using FocusedState instead of Introspect in order to prevent the keyboard from dismissing but that unfortunately causes a weird bounce animation which does not look pleasing to the eye.

here's an example of how i want it to function:

enter image description here


Solution

  • Since you already control textFieldShouldReturn, just do whatever you want to do in there.

    class TextFieldKeyboardBehavior: NSObject, UITextFieldDelegate, ObservableObject {
        var onSubmit: (() -> Void)?
        
        func textFieldShouldReturn(_ textField: UITextField) -> Bool {
            onSubmit?()
            return false
        }
    }
    

    Note that I made this an ObservableObject. This way, you can use a @StateObject to persist it, without having to create a new object every time the View struct is initialised.

    Then in introspect, you can just do:

    textFieldKeyboardBehavior.onSubmit = { ... }
    textField.delegate = textFieldKeyboardBehavior
    

    Here is a complete example:

    struct ContentView: View {
        @State private var ingredient = ""
        @State private var ingredients = [String]()
        @StateObject var textFieldKeyboardBehavior = TextFieldKeyboardBehavior()
        
        var body: some View {
            VStack {
                ForEach(ingredients, id: \.self) {
                    Text($0)
                }
                TextField("Enter an ingredient", text: $ingredient)
                    .introspect(.textField, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { textField in
                        textFieldKeyboardBehavior.onSubmit = {
                            withAnimation {
                                ingredients.append(ingredient)
                            }
                        }
                        textField.delegate = textFieldKeyboardBehavior
                    }
            }
        }
    }