Как у вас изменение значения TextField инициирует обновление другого фрагмента данных в SwiftUI? - PullRequest
0 голосов
/ 11 октября 2019

Я пытаюсь изучить Swift, SwiftUI и Combine, и я новичок в iOS в целом. В конце концов я хочу получить список, который вы можете искать, фильтровать и сортировать.

Пока что у меня работает фильтрация, когда я использую onEditingChanged в TextField, но для этого нужно нажать Enter. Я просто не могу понять, как вызвать activePeople для обновления при изменении TextField filterText, чтобы список activePeople фильтровал , пока вы печатаете.

Я получилверсия для работы, когда я фильтрую список в ForEach в представлении (см. закомментированный код), но в конечном итоге фильтрация и сортировка станут более сложными, и, кажется, имеет смысл иметь его вне этого представления. Дайте мне знать, если это не правильный подход по какой-либо причине.

Вот код на данный момент:

import Combine
import SwiftUI

class Model: ObservableObject {
    @Published var filterText: String = “”
    @Published var activePeople: [Person] = []

    private var allPeople : [Person] = [
        Person( id: 1000, name: “Alexa” ),
        Person( id: 1001, name: “Anaïs” ),
        Person( id: 1002, name: “Earl” ),
        Person( id: 1003, name: “Elba” ),
        Person( id: 1004, name: "Emil” ),
        Person( id: 1005, name: “Janeth” ),
        Person( id: 1006, name: “Joselyn” ),
        Person( id: 1007, name: “Lupita” ),
        Person( id: 1008, name: “Mellie” ),
        Person( id: 1009, name: “Vanita” ),
    ]

    init() {
        activePeople = allPeople
    }

    func filterList() {
        if ( filterText == “” ) {
            activePeople = allPeople
        } else {
            activePeople = allPeople.filter( { $0.name.localizedStandardContains( filterText ) } )
        }
    }

}

struct Person: Identifiable {
    var id: Int
    var name: String
}

import SwiftUI

struct ContentView: View {
    @EnvironmentObject private var model: Model

    var body: some View {
        VStack {
            Form {
                Section {
                    TextField(“Filter Text”, text: $model.filterText, onEditingChanged: {_ in self.model.filterList()}
                    )
                }
                Section {
                    Text( "Filtered by: \(model.filterText)” )
                    ForEach( model.activePeople ) { person in
//                        if ( self.model.filterText == “” || person.name.localizedStandardContains( self.model.filterText )) {
                            Text( person.name )
//                        }
                    }
                }
            }
        }
    }
}

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

}

Ответы [ 2 ]

0 голосов
/ 15 октября 2019

Чтобы наблюдать за изменениями ввода текстового поля, вы можете наблюдать переменную в модели. Удаление логики фильтрации из представления было бы, вероятно, хорошей идеей. Примером этого может быть начало наблюдения filterText, когда модель начинает работу. Кроме того, не забудьте сохранить ссылку на отмену, возвращаемую с sink. Здесь мы делаем каждый раз, когда изменяется значение из filterText (и это происходит из-за привязки текстового поля), мы фильтруем массив и присваиваем значение переменной activePeople

 var filterCancellable: AnyCancellable?

    init() {
        filterCancellable = $filterText.sink {[weak self] currentText in
            guard let strongSelf = self else { return } 
            strongSelf.activePeople = strongSelf.filterList(with: currentText)
        }
    }

    func filterList(with text: String) -> [Person] {
        if ( text == "" ) {
            return allPeople
        } else {
            return allPeople.filter( { $0.name.localizedStandardContains( text ) } )
        }
    }

. позже вы можете сделать только

var body: some View {
        VStack {
            Form {
                Section {
                    TextField("Filter Text", text: $model.filterText)
                }
                Section {
                    Text( "Filtered by: \(model.filterText)" )
                    ForEach( model.activePeople ) { person in
                        Text( person.name )
                    }
                }
            }
        }
    }
0 голосов
/ 11 октября 2019

Просто примените фильтрацию в вашем ContentView:

ForEach(model.activePeople.filter({ self.model.filterText.isEmpty ||
                                   $0.name.localizedStandardContains(self.model.filterText)})) { person in
                            Text( person.name )
                    }
...