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

时间:2019-09-07 12:45:21

标签: swiftui nsfetchrequest

我有一个使用@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")
         }
    }

}

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

2 个答案:

答案 0 :(得分:1)

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

您可以更容易地基于String数据过滤获取。但是,它的工作方式与下面的示例稍有不同,因为您需要像这样在View的init()中运行获取:

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

要完成您描述的任务,请单击栏上的按钮项,从而切换布尔,下面是我建议的示例:

此示例将完成您的目标,而无需更改获取谓词。它使用逻辑来根据数据模型中的条目和@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”的属性为String,另一个名为“ seeMe”的属性为Bool。然后运行它,按按钮创建两种类型的数据,然后单击顶部的条按钮项以选择要显示的数据。

我并不是最漂亮的示例,但是它应该演示您尝试完成的功能。

答案 1 :(得分:-1)

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

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