如何根据用户输入使用新谓词重新运行@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?的主要内容,如果未能解决你的问题,请参考以下文章