【问题标题】:ObservableObject with NSPredicate and a SearchBar in SwiftUISwiftUI 中带有 NSPredicate 和 SearchBar 的 ObservableObject
【发布时间】:2020-06-26 21:23:23
【问题描述】:

我正在尝试创建一个简单的 People 列表视图,顶部有一个搜索栏。

我设置了 UI,但编辑搜索栏不会调用 @State var searchText: StringdidSet 函数。

我相信我可能误解了@StateBindings 的工作原理?似乎在SearchBar 中绑定viewModel.$searchText 永远不会导致@State var searchText 更新。

怎么了?

struct ListView: View {
    @Environment(\.managedObjectContext) var context: NSManagedObjectContext
    
    @ObservedObject var viewModel: PeopleListViewModel
    
    var body: some View {
        VStack {
            SearchBar(text: viewModel.$searchText, showsCancelButton: .constant(false))
                .padding(Edge.Set(arrayLiteral: .top, .bottom), 0)
                .padding(Edge.Set(arrayLiteral: .leading, .trailing), 8)
            List(viewModel.people, id: \.id) { (person) in
                PersonCell(person: person)
            }
        }
    }
}

extension ListView {
    final class PeopleListViewModel: NSObject, NSFetchedResultsControllerDelegate, ObservableObject {
        private let peopleController: NSFetchedResultsController<Person>
        private let sortDescriptors = [NSSortDescriptor(keyPath: \Person.name, ascending: true)]
        
        @State var searchText: String = "" {
            didSet {
                updatePeople() // Never gets called! Maybe this shouldn't be a @State, but passed to the ViewModel in a different way?
            }
        }

        init(managedObjectContext: NSManagedObjectContext) {
            peopleController = Person.resultsController(context: managedObjectContext, sortDescriptors: sortDescriptors)
            super.init()
            peopleController.delegate = self
            try? peopleController.performFetch()
        }
        
        func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
            objectWillChange.send()
        }
        
        var people: [Person] {
            return peopleController.fetchedObjects ?? []
        }
        
        func updatePeople() {
            var predicate: NSPredicate?
            
            if !searchText.isEmpty {
                predicate = NSPredicate(format: "name CONTAINS[cd] %@", argumentArray: [searchText])
            }
            
            peopleController.fetchRequest.predicate = predicate
            try? peopleController.performFetch()
        }
    }
}

extension Person {
    static func resultsController(context: NSManagedObjectContext, sortDescriptors: [NSSortDescriptor] = []) -> NSFetchedResultsController<Person> {
        let request = NSFetchRequest<Person>(entityName: "Person")
        request.sortDescriptors = sortDescriptors.isEmpty ? nil : sortDescriptors
        return NSFetchedResultsController(fetchRequest: request, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
    }
}

【问题讨论】:

    标签: swift swiftui combine


    【解决方案1】:

    没关系,我自己发现了问题。我需要@Published,而不是@State,而$ 放错了地方。工作代码:

    struct ListView: View {
        @Environment(\.managedObjectContext) var context: NSManagedObjectContext
        
        @ObservedObject var viewModel: PeopleListViewModel
        
        var body: some View {
            VStack {
                SearchBar(text: $viewModel.searchText, showsCancelButton: .constant(false))
                    .padding(Edge.Set(arrayLiteral: .top, .bottom), 0)
                    .padding(Edge.Set(arrayLiteral: .leading, .trailing), 8)
                List(viewModel.people, id: \.id) { (person) in
                    PersonCell(person: person)
                }
            }
        }
    }
    
    extension ListView {
        final class PeopleListViewModel: NSObject, NSFetchedResultsControllerDelegate, ObservableObject {
            private let peopleController: NSFetchedResultsController<Person>
            private let sortDescriptors = [NSSortDescriptor(keyPath: \Person.name, ascending: true)]
            
            @Published var searchText: String = "" {
                didSet {
                    if oldValue != searchText {
                        updatePeople()
                    }
                }
            }
    
            init(managedObjectContext: NSManagedObjectContext) {
                peopleController = Person.resultsController(context: managedObjectContext, sortDescriptors: sortDescriptors)
                super.init()
                peopleController.delegate = self
                try? peopleController.performFetch()
            }
            
            func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
                objectWillChange.send()
            }
            
            var people: [Person] {
                return peopleController.fetchedObjects ?? []
            }
            
            func updatePeople() {
                var predicate: NSPredicate?
                
                if !searchText.isEmpty {
                    predicate = NSPredicate(format: "name CONTAINS[cd] %@", argumentArray: [searchText])
                }
                
                peopleController.fetchRequest.predicate = predicate
                try? peopleController.performFetch()
            }
        }
    }
    
    extension Person {
        static func resultsController(context: NSManagedObjectContext, sortDescriptors: [NSSortDescriptor] = []) -> NSFetchedResultsController<Person> {
            let request = NSFetchRequest<Person>(entityName: "Person")
            request.sortDescriptors = sortDescriptors.isEmpty ? nil : sortDescriptors
            return NSFetchedResultsController(fetchRequest: request, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2020-09-13
      • 1970-01-01
      • 2021-02-27
      • 1970-01-01
      • 2020-10-25
      • 1970-01-01
      • 2020-08-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多