如何根据用户输入使用新谓词重新运行@FetchRequest?

Posted

技术标签:

【中文标题】如何根据用户输入使用新谓词重新运行@FetchRequest?【英文标题】:How to rerun @FetchRequest with new predicate based on user input? 【发布时间】:2019-09-07 12:45:21 【问题描述】:

我有一个使用@FetchRequest 从CoreData 显示对象的列表,我想为用户提供一个条形按钮,单击该按钮将过滤显示的列表。 如何更改 @FetchRequest 谓词并动态重新运行它以使用过滤后的项目重建列表?

struct EmployeeListView : View 
    @FetchRequest(
        entity: Department.entity(),
        sortDescriptors: [NSSortDescriptor(keyPath: \Department.name, ascending: false)],
    )
    var depts: FetchedResults<Department>
    @Environment(\.managedObjectContext) var moc

    var body: some View 
        NavigationView 
            List 
                ForEach(depts, id: \.self)  dept in
                    Section(header: Text(dept.name)) 
                        ForEach(dept.employees, id: \.self)  emp in
                            Text(emp.name)
                        
                    
                
            
            .navigationBarTitle("Employees")
         
    


我知道如何提供过滤器,但我不知道如何更改属性包装谓词并重新运行获取请求。

【问题讨论】:

你发现了吗?快把我逼疯了 观看此视频youtu.be/g9VgkXVpxZU 谢谢,几天前就知道了 您还需要发布答案吗? 不会有伤害的,谢谢。 【参考方案1】:

您可以根据 fetch 谓词中的绑定更改结果,但使用 Bool vars,我发现这很难做到。原因是,在 CoreData 中测试 Bool 的谓词类似于 NSPredicate(format: "myAttrib == YES"),而您的 Bool 绑定变量将是 true 或 false,而不是 YES 或 NO...所以如果您 NSPredicate(format: "%K ==%@", #keypath(Entity.seeMe), seeMe.wrappedValue),这将始终为 false。也许我错了,但这就是我所经历的。

您可以更轻松地根据字符串数据过滤您的 fetch。但它的工作方式与我下面的示例略有不同,因为您需要像这样在 View 的 init() 中运行您的 fetch:

 @Binding var searchTerm:String
 var fetch: FetchRequest<Entity>
 var rows: FetchedResults<Entity>fetch.wrappedValue


 init(searchTerm:Binding<String>) 
   self._searchTerm = searchTerm
   self.fetch = FetchRequest(entity: Entity.entity(), sortDescriptors: [], predicate: NSPredicate(format: "%K == %@", #keyPath(Entity.attribute),searchTerm.wrappedValue))
 

要完成您所描述的任务,单击条形按钮项从而切换 Bool,以下示例是我推荐的:

此示例将在不更改 fetch 谓词的情况下实现您的目标。它使用逻辑来根据数据模型中的条目和@State 变量的值来决定是否显示一行数据。


import SwiftUI
import CoreData
import Combine

struct ContentView: View 

    @Environment(\.managedObjectContext) var viewContext
    @State var seeMe = false

    @FetchRequest(
        sortDescriptors: [NSSortDescriptor(keyPath: \Entity.attribute, ascending: true)],
        animation: .default)
    var rows: FetchedResults<Entity>

    var body: some View 

        NavigationView 
            VStack 


                ForEach(self.rows, id: \.self)  row in

                    Group() 
                        if (self.validate(seeMe: row.seeMe)) 
                            Text(row.attribute!)
                        
                    

                
                .navigationBarItems(leading:
                    Button(action: 
                        self.seeMe.toggle()
                    ) 
                        Text("SeeMe")
                    
                )

                Button(action: 
                    Entity.create(in: self.viewContext, attribute: "See Me item", seeMe: true)
                ) 
                    Text("add seeMe item")
                

                Button(action: 
                    Entity.create(in: self.viewContext, attribute: "Dont See Me item", seeMe: false)
                ) 
                    Text("add NON seeMe item")
                

            
        

    

    func validate(seeMe: Bool) -> Bool 
        if (self.seeMe && seeMe) 
            return true
         else if (!self.seeMe && !seeMe )
            return true
         else 
            return false
        
    



extension Entity 
    static func create(in managedObjectContext: NSManagedObjectContext,
                       attribute: String,
                       seeMe: Bool
    )

        let newEvent = self.init(context: managedObjectContext)
        newEvent.attribute = attribute
        newEvent.seeMe = seeMe
    

    static func save(in managedObjectContext: NSManagedObjectContext) 
        do 
            try  managedObjectContext.save()
         catch 
            // Replace this implementation with code to handle the error appropriately.
            // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
            let nserror = error as NSError
            fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
        
    


要使用此示例,请创建一个核心数据模型,其中包含一个名为“Entity”的实体和两个属性,一个名为“attribute”作为字符串,另一个名为“seeMe”作为 Bool。然后运行它,按按钮创建两种类型的数据,然后单击顶部的条形按钮项选择要显示的类型。

我不是最漂亮的例子,但它应该展示你想要完成的功能。

【讨论】:

因为它在 init 中,所以不是每次重新计算视图结构时都会重新获取任何状态更改? @FetchRequest 使 fetch 成为静态的,所以它只发生一次。 是的。但问题是如何不使其成为静态......您可以只获取整个数据集并根据逻辑显示您想要的内容,或者您​​可以根据输入重新运行您的获取。如上所述。【参考方案2】:

在获取请求上使用谓词来搜索具有特定名称的部门,如下所示:

struct ContentView: View 
    @State var deptName = "Computing Science"
    var body: some View 
        EmployeeListView(name:deptName)
    


struct EmployeeListView : View 
    @Environment(\.managedObjectContext) var managedObjectContext
    
    @FetchRequest var depts : FetchedResults<Department>
    
    init(name: name) 
        _depts = FetchRequest(sortDescriptors: [NSSortDescriptor(keyPath: \Department.name, ascending: false)], predicate: NSPredicate(format: "name = %@", name)
    
    
    var body: some View 
        NavigationView 
            List 
                ForEach(depts)  dept in
                    Section(header: Text(dept.name)) 
                        ForEach(dept.employees, id: \.self)  emp in
                            Text(emp.name)
                        
                    
                
            
            .navigationBarTitle("Employees")
         
    

【讨论】:

以上是关于如何根据用户输入使用新谓词重新运行@FetchRequest?的主要内容,如果未能解决你的问题,请参考以下文章

如何将用户输入转换为 gnu prolog 中的可重用谓词?

使用迭代器按谓词重新排列

Angular 2 根据用户输入重新加载当前组件

SwiftUI 中的动态过滤器(谓词)

如何刷新/重新加载以更新对谓词的更改并因此获取请求?

如何根据用户类型用相似的记录重新填充组合框