如果在 ItemView 中更新,则无法刷新 UI
Posted
技术标签:
【中文标题】如果在 ItemView 中更新,则无法刷新 UI【英文标题】:Cannot refresh UI if update in ItemView 【发布时间】:2020-02-27 08:28:38 【问题描述】:如果我只是将 ItemView 的代码放在 ForEach 中,触摸列表项的名称,切换属性检查,更改名称颜色,它就可以正常工作。当我将代码提取到ItemView中时,数据发生了变化,但UI没有刷新,为什么?
请注意,Item 是类,而不是结构。也许这很重要。
import CoreData
class Item: NSManagedObject, Identifiable
@NSManaged public var createAt: Date?
@NSManaged public var id: UUID?
@NSManaged public var name: String?
@NSManaged public var check: Bool
...
struct ContentView: View
@Environment(\.managedObjectContext) var context
@FetchRequest(entity: Item.entity(), sortDescriptors: [NSSortDescriptor(key: "createAt", ascending: false)]) var items: FetchedResults<Item>
var body: some View
NavigationView
VStack
List
ForEach(items) item in
// HStack
// Text("\(item.name!)").foregroundColor(item.check ? .red : .gray)
// Spacer()
// .onTapGesture
// item.check.toggle()
// try? self.context.save()
//
ItemView(item: item)
.onDelete(perform: indexSet in
let index = indexSet.first!
let item = self.items[index]
self.context.delete(item)
try? self.context.save()
)
Button(action:
let item = Item(context: self.context)
item.id = UUID()
item.createAt = Date()
item.name = "New Item"
try? self.context.save()
)
Text("New")
struct ItemView: View
@Environment(\.managedObjectContext) var context
var item: Item
var body: some View
HStack
Text("\(item.name!)").foregroundColor(item.check ? .red : .gray)
Spacer()
.onTapGesture
self.item.check.toggle()
try? self.context.save()
【问题讨论】:
【参考方案1】:这是因为ItemView
默认检测为相等,因为item
属性是指针。
这是可以做的(快照不可测试),所以只是粗糙(但它应该根据经验工作):
1) 如下更改ForEach
ForEach(items) item in
ItemView(item: item).equatable()
2) [!!!] 确保您的 Item
也是 Equatable
考虑到更改 check
属性
3) 使ItemView
相等
struct ItemView: View, Equatable
static func == (lhs: ItemView, rhs: ItemView) -> Bool
return lhs.item == rhs.item // preferable, but can be other appropriate
...
更新:替代方式
struct ItemView: View
@Environment(\.managedObjectContext) var context
var item: Item
@State private var refresh: Bool = true
var body: some View
HStack
Text("\(item.name!)").foregroundColor(item.check ? .red : .gray)
.background(Color.clear.opacity(self.refresh ? 1.0 : 0.0)) // just to be in view body
Spacer()
.onTapGesture
self.item.check.toggle()
try? self.context.save()
self.refresh.toggle() // force refresh self
【讨论】:
lhs 和 rhs 总是指向同一个地址,所以总是相等的。 @foolbear,这就是为什么项目应该明确符合 Equatable,覆盖 == 并比较所有属性,因为默认情况下只比较指针。 两个项目指向同一个地址,这两个项目的所有属性也相同,所以总是相等的。毫无意义。 @foolbear,这真的很奇怪......无论如何,总是有替代方式,请参阅更新部分。 是的,它会强制 ItemView 刷新。但这不是正常的方式。我发现 ItemView(item: item).id(item.check) 或 ItemView(item: item).id(UUID()) 工作正常,但我认为它们也不正常。以上是关于如果在 ItemView 中更新,则无法刷新 UI的主要内容,如果未能解决你的问题,请参考以下文章