【问题标题】:Cannot refresh UI if update in ItemView如果在 ItemView 中更新,则无法刷新 UI
【发布时间】: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()
        }
    }
}

【问题讨论】:

    标签: core-data swiftui


    【解决方案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()) 工作正常,但我认为它们也不正常。
    猜你喜欢
    • 2012-01-11
    • 2014-01-10
    • 1970-01-01
    • 1970-01-01
    • 2016-11-05
    • 2013-11-13
    • 2020-10-07
    • 2017-06-02
    • 2012-10-26
    相关资源
    最近更新 更多