【问题标题】:Passing an ObservableObject model through another ObObject?通过另一个 ObObject 传递一个 ObservableObject 模型?
【发布时间】:2020-04-28 13:29:22
【问题描述】:

我觉得我可以理解为什么我正在做的事情不起作用,但我仍在尝试围绕 Combine 和 SwiftUI 进行思考,因此欢迎任何帮助。

考虑这个例子:

在 UserDefaults 中存储一些字符串并使用这些字符串显示一些文本标签的单视图应用程序。共有三个按钮,一个用于更新标题,一个用于将两个 UserDefaults 存储的字符串更新为随机字符串。

视图是一个哑渲染器视图,标题字符串直接存储在 ObservableObject 视图模型中。视图模型有一个发布的属性,该属性包含对 UserSettings 类的引用,该类实现属性包装以将用户定义的字符串存储到 UserDefaults。

观察:

• 点击“设置新标题”会正确更新视图以显示新值

• 点击任一“设置用户值”按钮会在内部更改值,但视图不会刷新。 如果在这些按钮之一之后点击“设置新标题”,则在为标题更改重建视图主体时会显示新值。

查看:

import SwiftUI

struct ContentView: View {
    @ObservedObject var model = ViewModel()

    var body: some View {
        VStack {
            Text(model.title).font(.largeTitle)
            Form {
                Section {
                    Text(model.settings.UserValue1)
                    Text(model.settings.UserValue2)
                }

                Section {
                    Button(action: {
                        self.model.title = "Updated Title"
                    }) { Text("Set A New Title") }
                    Button(action: {
                        self.model.settings.UserValue1 = "\(Int.random(in: 1...100))"
                    }) { Text("Set User Value 1 to Random Integer") }
                    Button(action: {
                        self.model.settings.UserValue2 = "\(Int.random(in: 1...100))"
                    }) { Text("Set User Value 2 to Random Integer") }
                }

                Section {
                    Button(action: {
                        self.model.settings.UserValue1 = "Initial Value One"
                        self.model.settings.UserValue2 = "Initial Value Two"
                        self.model.title = "Initial Title"
                    }) { Text("Reset All") }
                }

            }
        }
    }
}

视图模型:

import Combine

class ViewModel: ObservableObject {

    @Published var title = "Initial Title"

    @Published var settings = UserSettings()

}

用户设置模型:

import Foundation
import Combine

@propertyWrapper struct DefaultsWritable<T> {
    let key: String
    let value: T

    init(key: String, initialValue: T) {
        self.key = key
        self.value = initialValue
    }

    var wrappedValue: T {
        get { return UserDefaults.standard.object(forKey: key) as? T ?? value }
        set { return UserDefaults.standard.set(newValue, forKey: key) }
    }
}



final class UserSettings: NSObject, ObservableObject {
    let objectWillChange = PassthroughSubject<Void, Never>()

    @DefaultsWritable(key: "UserValue", initialValue: "Initial Value One") var UserValue1: String {
        willSet {
            objectWillChange.send()
        }
    }
    @DefaultsWritable(key: "UserBeacon2", initialValue: "Initial Value Two") var UserValue2: String {
        willSet {
            objectWillChange.send()
        }
    }

}

当我在 UserSettings 中的 willSet { objectWillChange.send() } 上设置断点时,我看到 objectWillChange 消息会在我期望的时候发送给发布者,这告诉我问题可能是视图或视图模型不正确订阅它。我知道如果我在视图上将 UserSettings 作为 @ObservedObject 会起作用,但我觉得这应该在使用 Combine 的视图模型中完成。

我在这里缺少什么?我敢肯定这真的很明显......

【问题讨论】:

    标签: ios swift swiftui combine observableobject


    【解决方案1】:

    ObsesrvedObject监听@Published属性的变化,而不是更深的内部发布者,所以下面的思路是加入内部发布者,也就是PassthroughSubject,和@Published var settings,表示后者已经更新.

    使用 Xcode 11.2 / iOS 13.2 测试

    唯一需要更改的是ViewModel...

    class ViewModel: ObservableObject {
    
        @Published var title = "Initial Title"
        @Published var settings = UserSettings()
    
        private var cancellables = Set<AnyCancellable>()
        init() {
            self.settings.objectWillChange
                .sink { _ in
                    self.objectWillChange.send()
                }
                .store(in: &cancellables)
        }
    }
    

    【讨论】:

    • 这解决了它。谢谢,我想我没有正确地传递这个主题,但我无法理解正确的方式是什么样的。这很有道理!
    • 感谢您的解决方案....效果非常好..
    猜你喜欢
    • 2010-12-29
    • 1970-01-01
    • 2013-12-11
    • 2015-02-12
    • 2021-12-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多