使用@FetchRequest 绑定相关实体的 SwiftUI 不会更新视图
SwiftUI binding of related entity by using @FetchRequest does not update the view
使用 SwiftUI、CoreData 和 @FetchRequest 包装器,我根本没有在相关实体的 cahnegs 上获得任何视图更新。我设置了以下简单且有效的示例:
CoreData 模型如下所示:
GroupEntity -hasMany[items]-> ItemEntity
ItemEntity -hasOne[group]-> GroupEntity
我在 CoreData 自动生成的 类 中添加了两个扩展,以绕过这里讨厌的选项。底部的按钮创建一个包含三个项目的组。它们的名称相同,但由于其独特的 ID 而真正不同。该视图仅在下面列出了一个组及其所有相关项及其相应的值。轻敲一个项目会使它的价值增加一个。点击每个组部分底部的“更改 GroupName”按钮可更改组的名称。除了以下代码外,没有其他代码:
import SwiftUI
extension ItemEntity {
var wName: String { name ?? "nameless item"}
}
extension GroupEntity {
var wName: String { name ?? "nameless group" }
var aItems: [ItemEntity] {
let set = items as? Set<ItemEntity> ?? []
return set.sorted {
[=11=].wName < .wName
}
}
}
struct ContentView: View {
@Environment(\.managedObjectContext) var moc
@FetchRequest(entity: GroupEntity.entity(), sortDescriptors: []) var groups: FetchedResults<GroupEntity>
var body: some View {
List {
ForEach(groups, id: \.self) { group in
Section(header: Text("\(group.wName) >> \(group.aItems.reduce(0, {[=11=] + .value}))")) {
ForEach(group.aItems, id: \.id) { (item: ItemEntity) in
Text("\(item.wName) >> \(item.value)")
.onTapGesture {
item.value = item.value + 1
print("item value: \(item.value)")
// -> value changed & stored, but view IS NOT UPDATED
try? moc.save()
}
}
Button(action: {
group.name = group.wName + " [changed]"
// -> value changed,stored and view IS UPDATED
try? moc.save()
}) {
Text("change \(group.wName)")
}
}
}
Button(action: addGroupItems) {
Text("Add Group with Items")
}
}
}
func addGroupItems() {
let group = GroupEntity(context: moc)
group.id = UUID()
group.name = "G1"
let item1 = ItemEntity(context: moc)
item1.id = UUID()
item1.name = "I1"
item1.value = 1
group.addToItems(item1)
let item2 = ItemEntity(context: moc)
item2.id = UUID()
item2.name = "I2"
item2.value = 2
group.addToItems(item2)
let item3 = ItemEntity(context: moc)
item3.id = UUID()
item3.name = "I3"
item3.value = 3
group.addToItems(item3)
// save
try? moc.save()
}
}
每当更改组的名称时,视图都会自动刷新并使更改可见。但是当点击一个项目时,它的价值会增加(正如打印语句所证明的那样)但视图不会更新。请注意附加要求,该组的部分 header 还包括其所有项目值的计算总和。这也应该在项目更改时更新。这就是为什么简单地将 Item 导出到一个单独的 itemRowView(item: item)
并在其中将 item 声明为 @ObservedObject var item: ItemEntity
并不是这里的解决方案。
我假设,@FetchRequest 包装器以某种方式忽略了对相关项目的所有更改。但我无法相信它对相关数据毫无用处,因为那是 CoreDatas 的主要力量?
感谢大家的宝贵意见和想法!
仅凭想法 - 尝试以下操作
.onTapGesture {
item.value = item.value + 1
print("item value: \(item.value)")
// -> value changed & stored, but view IS NOT UPDATED
try? moc.save()
group.objectWillChange.send() // << here !!
}
好的,所以我找到了适合我的解决方案。添加一个发布者来监视 managedObjectContext 中的变化并在检测到变化时进行更新。
struct ContentView: View {
private var mocDidChanged = NotificationCenter.default.publisher(for: .NSManagedObjectContextObjectsDidChange)
/* ... */
var body: some View {
List { /* .. */ }.onReceive(self.mocDidChanged) { _ in
self.refresh()
}
}
func refresh() {
/* update related state variables here */
}
}
使用 SwiftUI、CoreData 和 @FetchRequest 包装器,我根本没有在相关实体的 cahnegs 上获得任何视图更新。我设置了以下简单且有效的示例:
CoreData 模型如下所示:
GroupEntity -hasMany[items]-> ItemEntity
ItemEntity -hasOne[group]-> GroupEntity
我在 CoreData 自动生成的 类 中添加了两个扩展,以绕过这里讨厌的选项。底部的按钮创建一个包含三个项目的组。它们的名称相同,但由于其独特的 ID 而真正不同。该视图仅在下面列出了一个组及其所有相关项及其相应的值。轻敲一个项目会使它的价值增加一个。点击每个组部分底部的“更改 GroupName”按钮可更改组的名称。除了以下代码外,没有其他代码:
import SwiftUI
extension ItemEntity {
var wName: String { name ?? "nameless item"}
}
extension GroupEntity {
var wName: String { name ?? "nameless group" }
var aItems: [ItemEntity] {
let set = items as? Set<ItemEntity> ?? []
return set.sorted {
[=11=].wName < .wName
}
}
}
struct ContentView: View {
@Environment(\.managedObjectContext) var moc
@FetchRequest(entity: GroupEntity.entity(), sortDescriptors: []) var groups: FetchedResults<GroupEntity>
var body: some View {
List {
ForEach(groups, id: \.self) { group in
Section(header: Text("\(group.wName) >> \(group.aItems.reduce(0, {[=11=] + .value}))")) {
ForEach(group.aItems, id: \.id) { (item: ItemEntity) in
Text("\(item.wName) >> \(item.value)")
.onTapGesture {
item.value = item.value + 1
print("item value: \(item.value)")
// -> value changed & stored, but view IS NOT UPDATED
try? moc.save()
}
}
Button(action: {
group.name = group.wName + " [changed]"
// -> value changed,stored and view IS UPDATED
try? moc.save()
}) {
Text("change \(group.wName)")
}
}
}
Button(action: addGroupItems) {
Text("Add Group with Items")
}
}
}
func addGroupItems() {
let group = GroupEntity(context: moc)
group.id = UUID()
group.name = "G1"
let item1 = ItemEntity(context: moc)
item1.id = UUID()
item1.name = "I1"
item1.value = 1
group.addToItems(item1)
let item2 = ItemEntity(context: moc)
item2.id = UUID()
item2.name = "I2"
item2.value = 2
group.addToItems(item2)
let item3 = ItemEntity(context: moc)
item3.id = UUID()
item3.name = "I3"
item3.value = 3
group.addToItems(item3)
// save
try? moc.save()
}
}
每当更改组的名称时,视图都会自动刷新并使更改可见。但是当点击一个项目时,它的价值会增加(正如打印语句所证明的那样)但视图不会更新。请注意附加要求,该组的部分 header 还包括其所有项目值的计算总和。这也应该在项目更改时更新。这就是为什么简单地将 Item 导出到一个单独的 itemRowView(item: item)
并在其中将 item 声明为 @ObservedObject var item: ItemEntity
并不是这里的解决方案。
我假设,@FetchRequest 包装器以某种方式忽略了对相关项目的所有更改。但我无法相信它对相关数据毫无用处,因为那是 CoreDatas 的主要力量?
感谢大家的宝贵意见和想法!
仅凭想法 - 尝试以下操作
.onTapGesture {
item.value = item.value + 1
print("item value: \(item.value)")
// -> value changed & stored, but view IS NOT UPDATED
try? moc.save()
group.objectWillChange.send() // << here !!
}
好的,所以我找到了适合我的解决方案。添加一个发布者来监视 managedObjectContext 中的变化并在检测到变化时进行更新。
struct ContentView: View {
private var mocDidChanged = NotificationCenter.default.publisher(for: .NSManagedObjectContextObjectsDidChange)
/* ... */
var body: some View {
List { /* .. */ }.onReceive(self.mocDidChanged) { _ in
self.refresh()
}
}
func refresh() {
/* update related state variables here */
}
}