Почему SwiftUI TextField внутри панели навигации принимает только один символ за раз - PullRequest
1 голос
/ 20 апреля 2020

Я хочу разрешить пользователю фильтровать данные в длинном списке, чтобы легче находить совпадающие заголовки.

Я поместил TextView внутри панели навигации:

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

У меня есть наблюдаемый объект, который реагирует на изменения в строке поиска:

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()) })
                }
            }
        }

Все отлично работает, за исключением того, что я должен нажать на поле после ввода каждой буквы. По какой-то причине фокус убирается из поля после ввода каждой буквы (если я не нажму на предложенное автозамену - вся строка будет правильно добавлена ​​в строку сразу)

1 Ответ

1 голос
/ 20 апреля 2020

Проблема в перестроении NavigationView полностью, что приводит к упадению фокуса текстового поля.

Вот рабочий подход. Протестировано с Xcode 11.4 / iOS 13.4

Идея состоит в том, чтобы избежать перестроения NavigationView, основываясь на знании того, что движок SwiftUI обновляет только измененных представлений, поэтому, используя декомпозицию, мы вносим изменения локально и переносим желаемые значения только между подпредставлениями, напрямую не влияющими на верх NavigationView, в результате чего последний сохраненный стенд.

demo

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())
    }
}
...