当具有不同值的相同 class object 作为数据源传递时,为什么不重新呈现视图

Why aren't views rerendered when the same class object with different values is passed as data source

我正在探索 SwiftUI,并且 运行 遇到了一些我不太明白的事情。

我创建了一个容器视图,可以按照 this post 中的描述获取数据,但我没有完全更改渲染视图中引用的 object,而是加载它的一些属性。

加载程序是容器视图观察到的ObservableObject。当加载程序指示它(s 值)已更改时,容器视图重新加载其 body 属性 并显示带有新数据的渲染视图。但是,当需要加载的object为class时,并不是body属性中的所有subview都重新加载。

这是我实现的一些"pseudo"代码。

protocol ValueLoader: Combine.ObservableObject {
    associatedtype Value

    var data: Value { get set }
    func load()
}

struct ValueLoadingContainerView<ValueConsumer: View, 
                                 ValueContainer: ValueLoader>: View {
    @ObservedObject var valueLoader: ValueContainer
    let containedView: (ValueContainer.Value) -> ValueConsumer

    init(_ loader: ValueContainer, 
         @ViewBuilder contained: @escaping (ValueContainer.Value) -> ValueConsumer) {
        self.valueLoader = loader
        self.containedView = contained
    }

    var body: some View {
        containedView(valueLoader.data)
            .onAppear(perform: load)
    }

    private func load() {
        self.valueLoader.load()
    }
}
class Object {
    let id: String
    var title: String?

    func load(from ...) {
        self.title = ...
    }
}
struct ConcreteLoader: ValueLoader {
    @Published var data: Object

    func load() {
        guard shouldLoad() else { return }  // To prevent infinite recursion
        ...
        // self.objectWillChange is synthesised by conforming to 
        // ObservableObject and having a property with @Published
        self.objectWillChange.send()          
        self.data.load(from: ...)
    }
}

struct ObjectRenderingView: View {
    let object: Object

    var body: some View {
        Text(object.title ?? "ObjectRenderingView is waiting...")
    }
}
let object = Object(id: "1", title: nil)

ValueLoadingContainer(ConcreteLoader(object), 
                      contained: { obj in
                          Text(obj.title ?? "Text is waiting...")     // 1
                          Divider()
                          ObjectRenderingView(object: obj)   // 2
                      })

当加载程序加载了 object 的属性时,它再次调用传递给 object 的 @ViewBuilder 闭包,但现在它的属性被加载了。

如果我添加 print 语句,我清楚地看到 contained @ViewBuilder 闭包被调用了两次:一次是卸载的 object 一次是加载的 object。这些都是一样的object,但是第二次,属性已经加载了。

Text 标签 (1) 已正确更新,从 "Text is waiting..." 更改为实际标题,但 ObjectRenderingView (2) 未更新其子视图。

ObjectRenderingViewinit 被新数据调用,但 body 属性 永远不会被访问。这向我表明 SwiftUI 认为数据没有改变并且不需要重新渲染。

我想我明白为什么它不起作用:obj 的身份没有改变,所以 SwiftUI 认为 ObjectRenderingView 不需要重新加载。由于值 obj.title 的标识已从 nil 更改为实际标题,因此重新加载 Text 视图。我不知道的是如何让 SwiftUI 也重新加载 ObjectRenderingView

谢谢!

应该是值类型。请查看 Xcode.

中 ObservedObject 的文档

If Value is not value semantic, the updating behavior for any views that make use of the resulting Binding is unspecified.