我觉得我可以理解为什么我的工作无法正常工作,但我仍在努力将自己的头围在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") }
}
}
}
}
}
ViewModel:
import Combine
class ViewModel: ObservableObject {
@Published var title = "Initial Title"
@Published var settings = UserSettings()
}
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的视图模型中完成。
我在这里想念什么?我敢肯定这很明显...
答案 0 :(得分:12)
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)
}
}