嵌套 ObservedObject 中的更改不会更新 UI

Sat*_*ima 8 swiftui combine

当我有一个嵌套的 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()
    }
}
Run Code Online (Sandbox Code Playgroud)

New*_*Dev 9

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

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

struct NestedObject {
   var flag = false
}
Run Code Online (Sandbox Code Playgroud)

这样做的原因是,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)
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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


wor*_*dog -1

试试这个,对我来说在 ios-15、catalyst 15、macos 12、使用 xcode 13 上效果很好:

        HStack {
            Text("Child:")
            Button(action: {
                state.objectWillChange.send()  // <--- here
                state.nestedState.flag.toggle()
            }, label: {
                Text(state.nestedState.flag ? "On" : "Off")
            })
        }
Run Code Online (Sandbox Code Playgroud)