@Published无法与环境对象和嵌套的ObservableObejct一起使用

时间:2020-06-09 22:49:41

标签: swift swiftui swiftui-environment

说我有UserSettings EnviornmentObject,它的属性之一是一个类,问题是当我更改该类的值时,EnviornmentObject将不会发布这些更改。我知道为什么,但是似乎找不到解决方法。

下面是显示问题的简化代码:

struct TestView: View {
    @EnvironmentObject var settings: UserSettings

    var body: some View {
        ZStack {
            Text("test: \(self.settings.ob.val)")

            VStack {
                // This is the only one that works and that makes sense, it changes the entire object
                Button(action: {
                    self.settings.changeOb(to: testOb(val: "1"))
                }) {
                    Text("Change object")
                }

                // From here on nothing works, I tried different ways to change the object value

                Button(action: {
                    self.settings.ob.changeVal(to: "2")
                }) {
                    Text("Change object's val")
                }

                Button(action: {
                    self.settings.changeVal(to: "3")
                }) {
                    Text("Change object's val V2")
                }

                Spacer()
            }
        }
    }
}

struct TestView_Previews: PreviewProvider {
    static var previews: some View {
        return ZStack {
            TestView().environmentObject(UserSettings(ob: testOb("abc")))
        }
    }
}

class testOb: ObservableObject {
    @Published private(set) var val: String

    init(val: String) {
        self.val = val
    }

    func changeVal(to: String) {
        self.val = to
    }
}

class UserSettings: ObservableObject {
    @Published private(set) var ob: testOb

    init(ob: testOb) {
        self.ob = ob
    }

    func changeOb(ob: testOb) {
        self.ob = ob
    }


    func changeVal(to: String) {
        self.ob.val(to: to)
    }
}

2 个答案:

答案 0 :(得分:1)

尝试这样的事情:

git rev-list $BASE..$HEAD | \
    xargs -L1 sh -c 'lines=$(git show --name-only $0 | grep -e somewhere/a -e somewhere/b | wc -l); \
    test $lines -ne 1 || { echo "bad commit $0"; false; }'

class UserSettings: ObservableObject { @Published var ob: testOb{ willSet{ observer.cancel() } didSet{ observer = ob.objectWillChange.sink(){self.objectWillChange.send()} } } var observer: AnyCancellable! init(ob: testOb) { self.ob = ob self.observer = nil self.observer = ob.objectWillChange.sink(){self.objectWillChange.send()} } func sendChange(){ } func changeOb(ob: testOb) { self.ob = ob } func changeVal(to: String) { self.ob.changeVal(to: to) } deinit { observer.cancel() } } 用于发送通知,而不用于收听。

或者您可以在子对象中存储到父对象的弱链接,并在子对象的@Published中调用parent.objectWillChange.send()

答案 1 :(得分:0)

我终于能够解决它,问题是苹果不支持嵌套的ObesrvableObject以及许多重要功能(这很可惜,但无论如何),但是据我所知,这已经在他们的议程上了。

解决方案是,每次对象更改时,我都必须手动通知,这可能会让您拥有更多的对象感到痛苦。

要进行通知,我们首先需要导入Combine,以便我们可以使用负责通知的AnyCancellable

这是更新的代码:

    // FIRST CHANGE
    import Combine

    struct TestView: View {
        @EnvironmentObject var settings: UserSettings

        var body: some View {
            ZStack {
                Text("test: \(self.settings.ob.val)")

                VStack {
                    // This is the only one that works and that makes sense, it changes the entire object
                    Button(action: {
                        self.settings.changeOb(to: testOb(val: "1"))
                    }) {
                        Text("Change object")
                    }

                    // From here on nothing works, I tried different ways to change the object value

                    Button(action: {
                        self.settings.ob.changeVal(to: "2")
                    }) {
                        Text("Change object's val")
                    }

                    Button(action: {
                        self.settings.changeVal(to: "3")
                    }) {
                        Text("Change object's val V2")
                    }

                    Spacer()
                }
            }
        }
    }

    struct TestView_Previews: PreviewProvider {
        static var previews: some View {
            return ZStack {
                TestView().environmentObject(UserSettings(ob: testOb("abc")))
            }
        }
    }

    class testOb: ObservableObject {
        @Published private(set) var val: String

        init(val: String) {
            self.val = val
        }

        func changeVal(to: String) {
            self.val = to
        }
    }

    class UserSettings: ObservableObject {
        @Published private(set) var ob: testOb

        // SECOND CHANGE, this is responsible to notify on changes
        var anyCancellable: AnyCancellable? = nil

        init(ob: testOb) {
            self.ob = ob

            // THIRD CHANGE, initialize our notifier
            anyCancellable = self.ob.objectWillChange.sink { (_) in
               self.objectWillChange.send()
            }
        }

        func changeOb(ob: testOb) {
            self.ob = ob
        }


        func changeVal(to: String) {
            self.ob.val(to: to)
        }
    }