【问题标题】:Is there a way to modify fetched results with a predicate after they are initialized?有没有办法在初始化后使用谓词修改获取的结果?
【发布时间】:2020-04-08 04:50:34
【问题描述】:

我正在尝试为现有的 CoreData 应用程序(简单的日志记录应用程序)构建搜索视图。 我使用 CoreData 存储了所有数据,并通过 @FetchRequest 获取:

@State private var searchPredicate: NSPredicate? = NSPredicate(format: "title contains[c] %@", "")

@FetchRequest( entity: Item.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \Item.title, ascending: true)],
      predicate: NSPredicate(format: "title contains[c] %@", "h")
    ) 
var items: FetchedResults<Item>

它现在只获取通过谓词测试的项目,在这种情况下是所有包含“h”的项目。 然后,我将结果显示在 SearchView 正文中的列表中:

List {

  ForEach(Items) { Item in

      ListViewItem(title: Item.title!, subTitle: Item.subTitle!, createdAt: "\(Item.createdAt!)")

     }
}

然后我创建了一个新类“Searchbar”,它在搜索视图中调用,应该根据搜索字段的输入创建一个谓词,然后将其作为绑定传递给父级,然后基于该谓词可以显示正确的项目。

在搜索视图中调用 VStack 顶部的搜索栏:

SearchBar(text: $searchText, predicate: $searchPredicate)

绑定会根据“searchBar”中的用户输入而变化:

func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {

    text = searchText
    predicate = NSPredicate(format: "title contains[c] %@", searchText)

 }

到目前为止一切都很好......

我现在遇到的问题是,我们有一个有效但不能在定义中的 @Fetchrequest 中调用的谓词,因为它会在初始化之前被调用。

@State private var searchPredicate: NSPredicate? = NSPredicate(format: "title contains[c] %@", "")


@FetchRequest(
    entity: Item.entity(),
    sortDescriptors: [
        NSSortDescriptor(keyPath: \Item.title, ascending: true)
    ],
    predicate: searchPredicate
) var items: FetchedResults<Item>

这给了我一个错误,即在 self 可用之前无法运行属性初始化程序,这是合乎逻辑的,但让我想知道并让我回到我的问题:有没有办法使用谓词修改获取的结果初始化后?

我还尝试在 ForEach() 语句中对获取的结果调用谓词相关方法,但它们似乎都不起作用。

如果有任何问题,请不要犹豫。

【问题讨论】:

    标签: search core-data swiftui predicate


    【解决方案1】:

    有没有办法在获取的结果之后用谓词修改它们 初始化了吗?

    嗯...不,不是您尝试这样做的方式,即使您尝试使用 NSFetchRequest 实例创建它,它是引用,并允许稍后更改谓词,这也行不通,因为 SwiftUI 的 FetchRequest 存储提供的获取请求的副本(或使用提供的参数创建自己的)......所以,不。但是……

    您可以将提供获取请求参数的视图与构造获取请求并显示结果的视图分开。

    这是一个方法演示(其中的重要部分),它使您可以使用不同的动态更改谓词获得结果:

    struct MasterView: View {
        @State var predicate: NSPredicate? = nil
        var body: some View {
            VStack {
                Button(action: { // button just for demo
                    self.predicate = NSPredicate(format: "title contains[c] %@", "h")
                }, label: { Text("Filter") })
                ResultView(predicate: self.predicate)
            }
        }
    }
    
    struct ResultView: View {
    
        @FetchRequest
        var events: FetchedResults<Event>
    
        @Environment(\.managedObjectContext)
        var viewContext
    
        init(predicate: NSPredicate?) {
            let request: NSFetchRequest<Event> = Event.fetchRequest()
            request.sortDescriptors = [NSSortDescriptor(keyPath: \Event.timestamp, ascending: true)]
            if let predicate = predicate {
                request.predicate = predicate
            }
            _events = FetchRequest<Event>(fetchRequest: request)
        }
    
        var body: some View {
            List {
                ForEach(events, id: \.self) { event in
        ...
    

    【讨论】:

    • 谢谢您的回答,我认为这与我想要实现的目标很接近。现在,当调用 ResultView 时,会同时传递一个谓词,然后对事件进行排序。但是我仍然面临无法动态传递谓词的问题。
    • 没关系,我刚刚让它工作了,你的代码工作得很好!非常感谢您的快速回复。
    【解决方案2】:

    由于我在其他任何地方都没有找到可行的解决方案,因此我决定发布完整的版本以及 Asperi 提供的出色答案。

    struct MasterView: View {
    
    
    @State var predicate: NSPredicate? = nil
    
    @State private var searchText = ""
    
    
    var body: some View {
    
        VStack {
    
    
            TextField("Search", text: $searchText, onEditingChanged: {_ in
    
    
                self.predicate = NSPredicate(format: "title contains[c] %@", "\(self.searchText)")
    
                print("THE PREDICATE: \(String(describing: self.predicate))")
    
    
                }, onCommit: {
    
                 print("onCommit")
    
             }).foregroundColor(.primary)
    
    
            SearchView(predicate: self.predicate)
    
        }
    }
    

    }

    还有 SearchView:

    struct SearchView: View {
    
    
    
    @State private var searchText = ""
    
    @Environment(\.managedObjectContext) var managedObjectContext
    
    
    
    @FetchRequest var items: FetchedResults<Item>
    
    
    init(predicate: NSPredicate?) {
    
    
        let request: NSFetchRequest<Item> = Item.fetchRequest() as! NSFetchRequest<Item>
    
        request.sortDescriptors = [NSSortDescriptor(keyPath: \Item.title, ascending: true)]
    
        if let predicate = predicate {
    
            request.predicate = predicate
    
        }
    
        _items = FetchRequest<Item>(fetchRequest: request)
    
    
    }
    
    
    
    
    
    var body: some View {
    
    
            VStack {
    
    
    
                List {
    ...
    

    【讨论】:

    • Jannis,你还有什么工作的例子吗?您发布的内容不完整,我无法使其正常工作。我想从你的例子中学习,因为我现在正在努力在 SwiftUI 中制作动态谓词
    【解决方案3】:

    作为对@Asperi 回答的内容(Swift 5.4 Xcode 12.5)的一点更新,您现在可以在 ResultView 中执行此操作:

    var fetchRequest: FetchRequest<Event>
    
    init(predicate: NSPredicate?) {
        fetchRequest = FetchRequest<Event>(entity: 
        Event.entity(), sortDescriptors: [NSSortDescriptor(keyPath: 
        \Event.timestamp, ascending: true)], predicate: predicate)
    }
    

    由于 FetchRequest 需要 NSPredicate?在其构造函数中:

    public init(entity: NSEntityDescription, sortDescriptors: [NSSortDescriptor], predicate: NSPredicate? = nil, animation: Animation? = nil)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-11-06
      • 1970-01-01
      • 1970-01-01
      • 2020-02-22
      • 2021-10-22
      • 2021-06-12
      • 1970-01-01
      • 2021-02-26
      相关资源
      最近更新 更多