我的第一次尝试是在
nsPredicate
中设置属性包装器的 .onAppear
动态属性,但如果视图因任何原因重新初始化,由 .onAppear
设置的谓词就会丢失。所以我又重新使用 init 模式。
这是我认为应该起作用(但不起作用)和确实起作用(无论多么神秘)的东西:
struct ItemEditView : View {
var item: Item
@FetchRequest(fetchRequest: Attribute.fetchRequestAllInOrder(), animation: .default)
var attributes: FetchedResults<Attribute>
init(item: Item) {
self.item = item
// This is how I would have expected to set the dynamic property at View initialization, however
// it crashes on this statement
attributes.nsPredicate = NSPredicate(format: "item == %@", item)
// Not sure why the below works and the above does not.
// It seems to work as desired, however it receives this runtime warning:
// "Context in environment is not connected to a persistent store coordinator"
$attributes.projectedValue.wrappedValue.nsPredicate = NSPredicate(format: "item == %@", item)
}
var body: some View {
List {
ForEach(attributes) { attribute in
Text("Name:\(attribute.name) Order:\(attribute.order)")
}
}
}
}
那么,为什么第一次对 nsPredicate 的赋值会崩溃呢?注释掉第一个之后,为什么第二个有效?警告消息是真正的问题吗?有一个更好的方法吗?似乎应该有一种简单的方法可以使用新的动态属性来做到这一点。
事实证明,(重新)设置
nsPredicate
中 @FetchRequest
的 onAppear
属性确实是正确的方法。但是,要实现此功能,您必须确保在调用 init()
后不会再次调用 View 的 onAppear
方法。今年 WWDC (WWDC21-10022) 的 Demystify SwiftUI 会议上有一些关于如何实现这一目标的宝贵提示。
FetchRequest
是仅在调用 DynamicProperty
之前准备就绪的 body
。如果您自己实现 DynamicProperty
,您将看到 update
函数,它是从 managedObjectContext
获取 @Environment
的地方,并且直到调用 body
之前环境才准备好。
实现同样目标的另一种方法是:
struct ItemEditView : View {
let item: Item
var request: FetchRequest(predicate: NSPredicate(value: false))
var results: FetchedResults<Attribute> {
request.wrappedValue.nsPredicate = NSPredicate(format: "item == %@", item)
request.wrappedValue.sortDescriptors = ...
return request.wrappedValue
}