当SwiftUI中的数据项发生更改时更新列表

当SwiftUI中的数据项发生更改时更新列表,swift,swiftui,Swift,Swiftui,我有一个Activities类,它采用了observegeobject协议,并且有一个items数组属性,该属性存储同样采用observegeobject的活动对象 class Activity: ObservableObject, Codable, Identifiable { let id = UUID() @Published var title: String @Published var description: String @Published va

我有一个Activities类,它采用了
observegeobject
协议,并且有一个
items
数组属性,该属性存储同样采用
observegeobject
的活动对象

class Activity: ObservableObject, Codable, Identifiable {
    let id = UUID()
    @Published var title: String
    @Published var description: String
    @Published var numberOfTimesCompleted: Int = 0

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)

        try container.encode(title, forKey: .title)
        try container.encode(description, forKey: .description)
        try container.encode(numberOfTimesCompleted, forKey: .numberOfTimesCompleted)
    }

    init(title: String, description: String) {
        self.title = title
        self.description = description
        numberOfTimesCompleted = 0
    }

    required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)

        title = try container.decode(String.self, forKey: .title)
        description = try container.decode(String.self, forKey: .description)
        numberOfTimesCompleted = try container.decode(Int.self, forKey: .numberOfTimesCompleted)
    }
}

class Activities: ObservableObject {
    @Published var items: [Activity] {
        didSet {
            let encoder = JSONEncoder()
            if let encoded = try? encoder.encode(items) {
                UserDefaults.standard.set(encoded, forKey: "items")
            }
        }
    }

    init() {
        if let items = UserDefaults.standard.data(forKey: "items") {
            let decoder = JSONDecoder()
            if let decoded = try? decoder.decode([Activity].self, from: items) {
                self.items = decoded
                return
            }
        }

        self.items = []
    }
}
我正在使用
列表
动态显示我的数组,就像这样

List {
  ForEach(activities.items) { activity in
    NavigationLink(destination: ActivityView(activity: activity)) {
      HStack {
        Text("\(activity.numberOfTimesCompleted)")
        VStack(alignment: .leading) {
          Text(activity.title)
          Text(activity.description)
        }
      }
    }
  }
}

当我将一个活动传递到另一个视图(在NavigationLink中)并编辑
numberOfTimesCompleted
属性时,当我导航回列表时,更改不会反映出来。如果我向列表中添加新的活动实例,列表将更新,新的
numberOfTimesCompleted
值将反映在我之前编辑的活动中。我有一种感觉,我很接近,但我只是在学习Swift和ObservieObject,因此我遗漏了一些东西。

这里的问题是列表视图有一个活动数组:
var items:[Activity]
,而孩子从该数组中有一个
活动。您已将活动
设置为可识别的
,并且该id是常量,因此所发生的是从数组中传入活动并对其进行修改,但就数组而言,没有任何更改:数组中的所有对象都具有完全相同的id,因此不会重新加载任何内容。您需要触发活动的重新加载。您可能想考虑使用环境对象或传递一个更改闭包,实际上执行数组上的突变,以便重新生成列表视图。只有当它能够检测到发布的数组与上一个值不同时,才会重新生成自身。

我愿意更改数据/类的结构。是否有一种“最佳实践”(或至少比我目前的做法更好)方法可以将项目发送到详细视图并对其进行编辑?我想我已经掌握了向下发送更改闭包的想法,但我很难想象Swift中的情况,以及如果我希望能够编辑活动实例上的多个属性,它会扩展到多大程度(我是否必须为我想要编辑的每个属性发送闭包,或者尝试创建一个通用闭包方法?)当我使用一个可编码表作为列表的基础时,我遇到了这个问题,而我用作标识符的字段从未更改。我将此字段添加到codable中,以便每次都生成一个唯一的id,并将其用作标识符,效果很好:let identifier=UUID()