Home > other >  TextField in a list not working well in SwiftUI
TextField in a list not working well in SwiftUI

Time:11-20

This problem is with SwiftUI for a iPhone 12 app, Using xcode 13.1. I build a List with TextField in each row, but every time i try to edit the contents, it is only allow me tap one time and enter only one character then can not keep enter characters anymore, unless i tap again then enter another one character.Did i write something code wrong with it?

    class PieChartViewModel: ObservableObject, Identifiable {
@Published var options = ["How are you", "你好", "Let's go to zoo", "OKKKKK", "什麼情況??", "yesssss", "二百五", "明天見"]
}

struct OptionsView: View {
    @ObservedObject var viewModel: PieChartViewModel
    
    var body: some View {
        NavigationView {
            List {
                ForEach($viewModel.options, id: \.self) { $option in
                    TextField(option, text: $option)
                }
            }
            .navigationTitle("Options")
            .toolbar {
                ToolbarItem(placement: .bottomBar) {
                    Button {
                        addNewOption()
                    } label: {
                        HStack {
                            Image(systemName: "plus")
                            Text("Create a new option")
                        }
                    }
                }
            }
        }
    }
    
    func addNewOption() {
        viewModel.options.insert("", at: viewModel.options.count)
    }
}

struct OptionsView_Previews: PreviewProvider {
    static var previews: some View {
        let pieChart = PieChartViewModel()
        OptionsView(viewModel: pieChart)
    }
}

CodePudding user response:

Welcome to StackOverflow! Your issue is that you are directly updating an ObservableObject in the TextField. Every change you make to the model, causes a redraw of your view, which, of course, kicks your focus from the TextField. The easiest answer is to implement your own Binding on the TextField. That will cause the model to update, without constantly redrawing your view:

struct OptionsView: View {
    // You should be using @StateObject instead of @ObservedObject, but either should work.
    @StateObject var model = PieChartViewModel()
    @State var newText = ""
    var body: some View {
        NavigationView {
            VStack {
                List {
                    ForEach(model.options, id: \.self) { option in
                        Text(option)
                    }
                }
                
                List {
                    //Using Array(zip()) allows you to sort by the element, but use the index.
                    //This matters if you are rearranging or deleting the elements in a list.
                    ForEach(Array(zip(model.options, model.options.indices)), id: \.0) { option, index in
                        // Binding implemented here.
                        TextField(option, text: Binding<String>(
                            get: {
                                model.options[index]
                            },
                            set: { newValue in
                                //You can't update the model here because you will get the same behavior
                                //that you were getting in the first place.
                                newText = newValue
                            }))
                            .onSubmit {
                                //The model is updated here.
                                model.options[index] = newText
                                newText = ""
                            }
                    }
                }
                .navigationTitle("Options")
                .toolbar {
                    ToolbarItem(placement: .bottomBar) {
                        Button {
                            addNewOption()
                        } label: {
                            HStack {
                                Image(systemName: "plus")
                                Text("Create a new option")
                            }
                        }
                    }
                }
            }
        }
    }
    
    func addNewOption() {
        model.options.insert("", at: model.options.count)
    }
}
  • Related