首页 > 解决方案 > 嵌套 ObservedObject 中的更改不会更新 UI

问题描述

当我有一个嵌套的 ObservedObject 时,嵌套对象的已发布属性的更改不会更新 UI,直到父对象发生某些事情。这是我的代码中的功能、错误(在 SwiftUI 中)还是错误?

这是一个简化的例子。单击父级的开/关按钮会立即更新 UI,但单击子级的开/关按钮不会更新,直到父级更新。

我正在运行 Xcode 12.5.1。

import SwiftUI

class NestedObject: NSObject, ObservableObject {
    @Published var flag = false
}
class StateObject: NSObject, ObservableObject {
    @Published var flag = false
    @Published var nestedState = NestedObject()
}

struct ContentView: View {
    @ObservedObject var state = StateObject()
    var body: some View {
        VStack {
            HStack {
                Text("Parent:")
                Button(action: {
                    state.flag.toggle()
                }, label: {
                    Text(state.flag ? "On" : "Off")
                })
            }
            HStack {
                Text("Child:")
                Button(action: {
                    state.nestedState.flag.toggle()
                }, label: {
                    Text(state.nestedState.flag ? "On" : "Off")
                })
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

标签: swiftuicombine

解决方案


@ObservedObject@StateObject在更新时更新视图ObservableObject。这发生在@Published更改属性或直接调用objectWillChange.send().

因此,“正常”(也是最简单的)方法是对属性使用值类型,例如structa @Published

struct NestedObject {
   var flag = false
}

这样做的原因是,NestedObject当它的属性被修改时,整体会发生变化,因为struct它是一个值类型。相反,引用类型class在其属性被修改时不会改变(即引用保持不变)。


但是,有时你可能需要它是一个引用类型的对象,因为它可能有自己的生命周期,等等......

在这种情况下,您绝对可以只调用state.objectWillChange.send(),但这仅在视图启动更改时才有效,而不是在嵌套对象启动更改时。在我看来,这里最好的通用方法是使用嵌套的内部视图@ObservedObject来观察内部对象的变化:

struct ContentView: View {

    private struct InnerView: View {
        @ObservedObject var model: NestedObject
        var body: some View {
            Text("Child:")
            Button(action: {
                model.flag.toggle()
            }, label: {
                Text(model.flag ? "On" : "Off")
            })
        }
    }

    @StateObject var state = OuterObject() // see comments 1, 2 below

    var body: some View {
        VStack {
            HStack {
                Text("Parent:")
                Button(action: {
                    state.flag.toggle()
                }, label: {
                    Text(state.flag ? "On" : "Off")
                })
            }
            HStack {
                InnerView(model: state.nestedObject)
            }
        }
    }
}

1. 你不应该调用你的类StateObject,因为它与StateObjectSwiftUI 的属性包装器冲突。我将其重命名为OuterObject.

2. 此外,如果您在视图中实例化对象,您应该使用@StateObject而不是。@ObservedObject


推荐阅读