SwiftUI 不能可靠地将更改传播到子视图

SwiftUI does not reliably propagate changes to child view

我在我的 SwiftUI 代码中观察到一个奇怪的行为,并将其缩小到以下最小示例。

给定此示例存储,其中包含书籍模型结构数组。

struct Book: Identifiable {
    let id: UUID
    var likes: Int
    var unusedProperty: String = ""
}

extension Book: Equatable {
    static func == (lhs: Book, rhs: Book) -> Bool {
        return lhs.id == rhs.id
    }
}

class MyStorage: ObservableObject {

    @Published var books: [Book] = [
        .init(id: .init(uuidString: "B2A44450-BC03-47E6-85BE-E89EA69AF5AD")!, likes: 0),
        .init(id: .init(uuidString: "F5AB9D18-DF73-433E-BB48-1C757CB6F8A7")!, likes: 0)
    ]

    func addLike(to book: Book) {
        for i in books.indices where books[i].id == book.id {
            books[i].likes += 1
        }
    }
}

并在这个简单的视图层次结构中使用它:

struct ReducedContentView: View {
    @StateObject var storage: MyStorage = MyStorage()

    var body: some View {
        VStack(spacing: 8) {
            ForEach(storage.books) { book in
                HStack {
                    VStack(alignment: .leading) {
                        Text("Top-Level: \(book.likes)")
                        BookView(book: book)
                    }
                    Spacer()
                    Button("Add Like") {
                        storage.addLike(to: book)
                    }
                }.padding(.horizontal)
            }
        }
    }
}

struct BookView: View {
    let book: Book

    var body: some View {
        Text("Nested: \(book.likes)")
            .foregroundColor(.red)
    }
}

likes 属性 的任何更改都不会传播到 BookView,只会传播到“顶级”Text

现在,如果我更改以下其中一项,它将起作用:

最后一个选项是我可以在我的生产代码中采用的东西 - 尽管如此,我真的很想了解这里发生了什么,所以任何提示都将不胜感激。

这是您自定义 Equatable 一致性的结果:

extension Book: Equatable {
    static func == (lhs: Book, rhs: Book) -> Bool {
        return lhs.id == rhs.id
    }
}

如果删除它,它将按预期工作。

在您当前的代码中,您是说如果两个 Book 具有相同的 ID,则它们 相等 。我怀疑你实际上并不是说它们真正平等——你只是说它们是同一本书

你的第二个选项(“将 && lhs.likes == rhs.likes 添加到 Equatable 一致性(这不是预期的)”)本质上只是使用系统生成的综合 Equatable 一致性,因为 unusedProperty 未使用 - 因此,如果您要使用第二个选项,您也可以完全删除自定义 == 函数。

我认为这里要做的决定是你是否真的想告诉系统一个谎言(两个 Book,无论它们的其他属性如何,都 相等 如果它们共享相同的 id) 或者您是否应该让系统自行判断项目是否相等。