SwiftUI 无法可靠地将更改传播到子视图

Jon*_*s W 1 ios swift swiftui combine

我在 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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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)
    }
}
Run Code Online (Sandbox Code Playgroud)

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

在此输入图像描述

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

  • 删除unusedProperty(生产中需要的)
  • 添加&& lhs.likes == rhs.likes到 Equatable 一致性(这不是有意的)
  • 修改BookView为接受@Binding var book: Book而不是let

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

jn_*_*pdx 5

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

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

Run Code Online (Sandbox Code Playgroud)

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

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

您的第二个选项(“添加&& lhs.likes == rhs.likesEquatable一致性(这不是有意的)”)本质上只是使用Equatable系统生成的综合一致性,因为unusedProperty没有使用 - 所以,如果您要使用第二个选项,您也可以只需完全删除自定义==功能即可。

我认为这里要做的决定是你是否真的想告诉系统一个谎言(两个Books,无论它们的其他属性是什么,如果它们共享相同的属性,那么它们是相等的id),或者你是否应该让系统做它自己的工作告诉项目是否相等。