当 SwiftUI 列表选择更改时,如何防止 Core Data 获取请求重置其谓词?

Gen*_*ich 5 core-data swiftui

示例应用程序具有实体的核心数据模型,Garden该模型与实体具有一对多关系FruitList用户被邀请使用 SwiftUI在恒定编辑模式下和选择参数集在花园里采摘水果。用户的选择将反映在核心数据关系中。问题是,当用户搜索某些内容然后尝试选择水果时,搜索将被重置。我假设这是预定义的行为,并想知道如何覆盖它,以便搜索持续存在,即用户通过搜索设置的谓词仍然处于活动状态,并且列表仍然需要过滤。

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)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

苹果开发者门户网站上有一篇文章讨论了类似的问题。帖子中提供的解决方案是传入谓词并将描述符从父级排序到视图的初始值设定项中。我已经尝试过了,但并不能解决问题。

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)
}
Run Code Online (Sandbox Code Playgroud)
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)
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

mal*_*hal 2

我尝试了您的项目并放置了一些断点,问题是当进行选择时,ContentView 的主体被调用,它使用默认值FetchRequest而不是带有搜索谓词的FruitPicker 来初始化FruitPicker。

在查看您的编码时,我注意到一些非标准的事情。PersistenceController 应该是一个结构体,而不是 ObservableObject(请参阅检查了核心数据的默认应用程序模板)。计算绑定的使用对我来说看起来很奇怪,但如果它有效的话就很酷。

要解决此问题,您可以将搜索和列表分成 2 个视图,以便使用新的搜索词初始化列表,然后FetchRequest始终正确,例如

struct FruitPickerSearch: View {
    @State var searchText = ""

    var body: some View {
        FruitPicker(searchText: searchText)
        .searchable(text: $searchText)
        .environment(\.editMode, .constant(EditMode.active))
        .navigationTitle("Garden")
    }
}

struct FruitPicker: View {
    private var fetchRequest: FetchRequest<Fruit>
    private var fruits: FetchedResults<Fruit> {
        fetchRequest.wrappedValue
    }
 
    init(searchText: String){
        let predicate = searchText.isEmpty ? nil : NSPredicate(format: "name CONTAINS[cd] %@", searchText)
        fetchRequest = FetchRequest(sortDescriptors: [NSSortDescriptor(keyPath: \Fruit.name, ascending: true)],
        predicate: predicate,
        animation: .default)
    }   

    var body: some View {
        List(fruits) { fruit in
            Text(fruit.name ?? "")
        }
    }
}
Run Code Online (Sandbox Code Playgroud)