SwiftUI 中的动态过滤器(谓词)
Posted
技术标签:
【中文标题】SwiftUI 中的动态过滤器(谓词)【英文标题】:dynamic filters (predicate) in SwiftUI 【发布时间】:2019-12-27 13:39:57 【问题描述】:我正在使用 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
//
//
我还有一个名为 Singer 的实体,该实体有 2 个属性:firstName 和 lastName,它们都是字符串。 上面的示例在模拟器中似乎可以正常工作,但在 Xcode 中使用 Preview 时会导致应用崩溃。
我将不胜感激,例如:
指出要更改示例代码的哪一部分以避免预览中的错误 在 SwiftUI 中使用动态谓词的另一种方式的简单示例 链接到有关 SwiftUI 中的动态过滤器的教程【问题讨论】:
【参考方案1】:-
要使 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"
。当需要新过滤时,只需将此私有 @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)
【讨论】:
非常感谢,Gergely。您的第一个答案帮助我摆脱了错误! :) 至于第二个答案 - 这听起来很有希望。但是我在实施上有问题。在线self.predicate = "S"
我收到错误Cannot assign value of type 'String' to type 'NSPredicate?'
好的,我想通了。我需要使用self.predicate = NSPredicate(format: "lastName BEGINSWITH %@", "S")
而不是self.predicate = "A"
:) 最后一个问题。我应该如何在 FilteredList_Previews 中定义 content: <#(T) -> Content#>
以使其工作?
@mallow 用预览更新了我的答案。【参考方案2】:
这是另一种方式:
import CoreData
import SwiftUI
struct FilteredList: View
@FetchRequest var singers: FetchedResults<Singer>
init(lastName: String)
_singers = FetchRequest(sortDescriptors: [], predicate: NSPredicate(format: "lastName BEGINSWITH %@", lastName))
var body: some View
List(singers) singer in
Text("\(singer.firstName ?? "Unknown") \(singer.lastName ?? "Unknown")")
请注意,每次 View 初始化且 FetchRequest 初始化时,都会命中数据库,因此您可能需要将 fetch 提升到超级视图中。
【讨论】:
以上是关于SwiftUI 中的动态过滤器(谓词)的主要内容,如果未能解决你的问题,请参考以下文章
SwiftUI:如何将 CoreData 与动态过滤器相加?