我正在使用 SwiftUI 和 CoreData 编写一个 iOS 应用程序。我现在正在努力解决一个问题几天。如何在 SwiftUI 中根据用户输入使用动态变化的谓词来制作动态过滤器?
我按照本教程学习了动态过滤器和 CoreData:https://www.hackingwithswift.com/quick-start/ios-swiftui/dynamically-filtering-fetchrequest-with-swiftui
经过一些小的更改后,我得到了以下代码。 ContentView.swift:
import SwiftUI
struct ContentView: View {
@Environment(\.managedObjectContext) var moc
@State var lastNameFilter = "A"
var body: some View {
VStack {
FilteredList(predicate: lastNameFilter)
Button("Add Examples") {
let taylor = Singer(context: self.moc)
taylor.firstName = "Taylor"
taylor.lastName = "Swift"
let ed = Singer(context: self.moc)
ed.firstName = "Ed"
ed.lastName = "Sheeran"
let adele = Singer(context: self.moc)
adele.firstName = "Adele"
adele.lastName = "Adkins"
try? self.moc.save()
}
Button("Show A") {
self.lastNameFilter = "A"
}
Button("Show S") {
self.lastNameFilter = "S"
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
FilteredList.swift:
import CoreData
import SwiftUI
struct FilteredList: View {
var predicate:String
var fetchRequest: FetchRequest<Singer>
var singers: FetchedResults<Singer>{fetchRequest.wrappedValue}
var body: some View {
List(singers, id: \.self) { singer in
Text("\(singer.firstName ?? "Unknown") \(singer.lastName ?? "Unknown")")
}
}
init(predicate: String) {
self.predicate = predicate
self.fetchRequest = FetchRequest<Singer>(entity: Singer.entity(), sortDescriptors: [], predicate: NSPredicate(format: "lastName BEGINSWITH %@", predicate))
}
}
//struct FilteredList_Previews: PreviewProvider {
// static var previews: some View {
// }
//}
我还有 1 个名为 Singer 的实体,该实体有 2 个属性:firstName 和 lastName,这两个属性都是字符串。 上面的示例在模拟器中似乎运行良好,但在 Xcode 中使用预览时会导致应用程序崩溃。
我希望得到任何帮助,例如:
- 指出要更改示例代码的哪一部分以避免预览中出现错误
- 在 SwiftUI 中使用动态谓词的另一种方式的简单示例
- 链接到有关 SwiftUI 中动态过滤器的教程
最佳答案
- 要使 ContentView 预览正常工作,您应该编写如下内容:
static var previews: some View {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let taylor = Singer(context: context)
taylor.firstName = "Taylor"
taylor.lastName = "Swift"
let ed = Singer(context: context)
ed.firstName = "Ed"
ed.lastName = "Sheeran"
let adele = Singer(context: context)
adele.firstName = "Adele"
adele.lastName = "Adkins"
return ContentView().environment(\.managedObjectContext, context)
}
- 您可以实现更通用的 FilteredList 类型,其中仅提供列表的谓词(可选的排序描述符)。示例:
struct FilteredList<T: NSManagedObject, Content: View>: View {
var fetchRequest: FetchRequest<T>
var items: FetchedResults<T> { fetchRequest.wrappedValue }
let content: (T) -> Content
var body: some View {
List(items, id: \.self) { item in
self.content(item)
}
}
init(predicate: NSPredicate?, sortDescriptors: [NSSortDescriptor] = [], @ViewBuilder content: @escaping (T) -> Content) {
fetchRequest = FetchRequest<T>(entity: T.entity(), sortDescriptors: sortDescriptors, predicate: predicate)
self.content = content
}
}
将此新的 FilteredList
类型与 @State private var predicate: NSPredicate?
一起使用,而不是 @State var lastNameFilter = "A"
。当需要新的过滤时,只需将此私有(private) @State 属性设置为新谓词,列表就会相应更新。
具体用法是:
FilteredList(predicate: predicate) { (singer: Singer) in
Text("\(singer.firstName ?? "") \(singer.lastName ?? "")")
}
FilteredList 预览:
static var previews: some View {
FilteredList(predicate: nil) { (singer: Singer) in
Text("\(singer.firstName ?? "") \(singer.lastName ?? "")")
}.environment(\.managedObjectContext, (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext)
}
关于ios - SwiftUI 中的动态过滤器(谓词),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59501638/