当 SwiftUI 列表选择更改时,如何防止 Core Data 获取请求重置其谓词?
示例应用的核心数据模型为 Garden
与 Fruit
实体具有一对多关系的实体。邀请用户在恒定编辑模式下使用 SwiftUI List
在花园中采摘水果,并设置选择参数。用户的选择将反映在核心数据关系中。问题是,当用户搜索某些内容然后尝试选择水果时,搜索将被重置。我假设这是预定义的行为,并想知道如何覆盖它,以便搜索持续存在,即用户通过搜索设置的谓词仍然处于活动状态,并且列表仍然需要过滤。
struct FruitPicker: View {
@FetchRequest(
sortDescriptors: [NSSortDescriptor(keyPath: \Fruit.name, ascending: true)],
animation: .default)
private var fruits: FetchedResults<Fruit>
@Binding var selection: Set<Fruit>
@State var searchText = ""
var body: some View {
List(fruits, id: \.self, selection: $selection) {
Text($0.name ?? "")
}
.searchable(text: query)
.environment(\.editMode, .constant(EditMode.active))
.navigationTitle("Garden")
}
var query: Binding<String> {
Binding {
searchText
} set: { newValue in
searchText = newValue
fruits.nsPredicate = newValue.isEmpty ? nil : NSPredicate(format: "name CONTAINS[cd] %@", newValue)
}
}
}
有一个Apple 开发者门户网站上的帖子涉及到了类似的问题。帖子中提供的解决方案是传入谓词并将描述符从父级排序到视图的初始值设定项中。我已经尝试过了,但并不能解决问题。
init(selection: Binding<Set<Fruit>>, sortDescriptors: [NSSortDescriptor], predicate: NSPredicate?) {
_selection = selection
let entity = Fruit.entity()
_fruits = FetchRequest<Fruit>(entity: entity, sortDescriptors: sortDescriptors, predicate: predicate, animation: .default)
}
struct ContentView: View {
@ObservedObject var garden: Garden
var body: some View {
NavigationView {
NavigationLink("Pick Fruits in the Garden
The sample app has a Core Data model of Garden
entity that has a to-many relationship with Fruit
entity. User is invited to pick fruits in the garden using a SwiftUI List
in constant edit mode with a selection parameter set. The user selection will be reflected in the Core Data relationship. The issue is that when the user searches for something and then attempts to select a fruit, the search is reset. I'm assuming this is predefined behavior and wondering how to override it so the search persists i.e. the predicate that the user set via search is still active, and the list remains to be filtered.
struct FruitPicker: View {
@FetchRequest(
sortDescriptors: [NSSortDescriptor(keyPath: \Fruit.name, ascending: true)],
animation: .default)
private var fruits: FetchedResults<Fruit>
@Binding var selection: Set<Fruit>
@State var searchText = ""
var body: some View {
List(fruits, id: \.self, selection: $selection) {
Text($0.name ?? "")
}
.searchable(text: query)
.environment(\.editMode, .constant(EditMode.active))
.navigationTitle("Garden")
}
var query: Binding<String> {
Binding {
searchText
} set: { newValue in
searchText = newValue
fruits.nsPredicate = newValue.isEmpty ? nil : NSPredicate(format: "name CONTAINS[cd] %@", newValue)
}
}
}
There is a post on the Apple developer portal that goes into a somewhat similar issue. The solution that is offered in the post is to pass in a predicate and sort descriptors from the parent into the view's initializer. I've tried that and it does not solve the issue.
init(selection: Binding<Set<Fruit>>, sortDescriptors: [NSSortDescriptor], predicate: NSPredicate?) {
_selection = selection
let entity = Fruit.entity()
_fruits = FetchRequest<Fruit>(entity: entity, sortDescriptors: sortDescriptors, predicate: predicate, animation: .default)
}
struct ContentView: View {
@ObservedObject var garden: Garden
var body: some View {
NavigationView {
NavigationLink("Pick Fruits in the Garden ????") {
FruitPicker(selection: $garden.fruits.setBinding(), sortDescriptors: [NSSortDescriptor(keyPath: \Fruit.name, ascending: true)], predicate: nil)
}
}
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我尝试了你的项目,设置了一些断点,问题是当进行选择时,
ContentView
的主体被调用,它使用默认的FetchRequest<初始化
FruitPicker
/code> 而不是带有搜索谓词的。我还注意到您的代码中有一些非标准的东西。
PersistenceController
应该是一个结构体,而不是ObservableObject
(请参阅检查了核心数据的默认应用程序模板)。计算绑定的使用对我来说看起来很奇怪,但如果它有效的话就很好。要解决此问题,您可以将搜索和列表分解为 2 个视图,以便使用新的搜索词初始化列表,然后
FetchRequest
将始终正确,例如I tried your project, made some breakpoints and the problem is when a selection is made,
ContentView
's body is called, which initsFruitPicker
with the defaultFetchRequest
instead of the one with the search predicate.I also noticed some non-standard things in your code.
PersistenceController
should be a struct instead of anObservableObject
(see the default app template with core data checked). The use of computed bindings looks odd to me but fine if it works.To fix the problem you could break up the search and the list into 2 Views, so that the List is init with the new search term and then the
FetchRequest
will always be correct, e.g.因为每次您在列表中选择一个项目时,视图都会更新,从而导致您的谓词重置为其初始状态。
另一种方法可能是使用 Equatable 协议。让您的子视图符合此协议,设置您自己的规则,并在父视图上使用
EquatableView()
包装您的子视图。关于这个问题,你可以参考我的另一篇文章:Why does @EnvironmentObject Force Re-initialization of View?Because every time you select an item in the list, the view will get updated, thus cause your predicate to reset to its initial state.
Another approach would probably be using the
Equatable
protocol. Make your subview conforms to this protocol, set your own rules, and wrap your subview withEquatableView()
on the parent view. You can refer to my other post regarding this problem: Why Does @EnvironmentObject Force Re-initialization of View?