Why does a SwiftUI TextField inside a navigation bar only accept input one character at a time

白昼怎懂夜的黑 提交于 2020-05-27 13:12:45

问题


I want to allow the user to filter data in a long list to more easily find matching titles.

I have placed a TextView inside my navigation bar:

.navigationBarTitle(Text("Library"))
.navigationBarItems(trailing: TextField("search", text: $modelData.searchString)

I have an observable object which responds to changes in the search string:

class DataModel: ObservableObject {
   @Published var modelData: [PDFSummary]
   @Published var searchString = "" {
            didSet {
                if searchString == "" {
                    modelData =  Realm.studyHallRealm.objects(PDFSummary.self).sorted(by: { $0.name < $1.name })
                } else {
                    modelData =  Realm.studyHallRealm.objects(PDFSummary.self).sorted(by: { $0.name < $1.name }).filter({ $0.name.lowercased().contains(searchString.lowercased()) })
                }
            }
        }

Everything works fine, except I have to tap on the field after entering each letter. For some reason the focus is taken away from the field after each letter is entered (unless I tap on a suggested autocorrect - the whole string is correctly added to the string at once)


回答1:


The problem is in rebuilt NavigationView completely that result in dropped text field focus.

Here is working approach. Tested with Xcode 11.4 / iOS 13.4

The idea is to avoid rebuild NavigationView based on knowledge that SwiftUI engine updates only modified views, so using decomposition we make modifications local and transfer desired values only between subviews directly not affecting top NavigationView, as a result the last kept stand.

class QueryModel: ObservableObject {
    @Published var query: String = ""
}

struct ContentView: View {
    // No QueryModel environment object here - 
    //                implicitly passed down. !!! MUST !!!

    var body: some View {
        NavigationView {
            ResultsView()
                .navigationBarTitle(Text("Library"))
                .navigationBarItems(trailing: SearchItem())
        }
    }
}

struct ResultsView: View {
    @EnvironmentObject var qm: QueryModel // << injected here from top
    var body: some View {
        VStack {
            Text("Search: \(qm.query)") // receive query string
        }
    }
}

struct SearchItem: View {
    @EnvironmentObject var qm: QueryModel // << injected here from top
    @State private var query = "" // updates only local view

    var body: some View {
        let text = Binding(get: { self.query }, set: {
            self.query = $0; self.qm.query = $0;       // transfer query string
        })
        return TextField("search", text: text)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView().environmentObject(QueryModel())
    }
}


来源:https://stackoverflow.com/questions/61325443/why-does-a-swiftui-textfield-inside-a-navigation-bar-only-accept-input-one-chara

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!