我有一个符合@ObservableObject协议的类,并使用@Published属性包装器通过其自己的变量从其创建了一个子类来管理状态。
使用子类时,似乎会忽略@published属性包装器。有人知道这是否是预期的行为,是否有解决方法?
我正在运行iOS 13 Beta 8和xCode Beta 6。
这是我所看到的一个例子。在MyTestObject
上更新TextField时,将使用aString值正确更新Text视图。如果我更新MyInheritedObject
TextField,则Text视图中的anotherString值不会更新。
import SwiftUI
class MyTestObject: ObservableObject {
@Published var aString: String = ""
}
class MyInheritedObject: MyTestObject {
@Published var anotherString: String = ""
}
struct TestObserverWithSheet: View {
@ObservedObject var myTestObject = MyInheritedObject()
@ObservedObject var myInheritedObject = MyInheritedObject()
var body: some View {
NavigationView {
VStack(alignment: .leading) {
TextField("Update aString", text: self.$myTestObject.aString)
Text("Value of aString is: \(self.myTestObject.aString)")
TextField("Update anotherString", text: self.$myInheritedObject.anotherString)
Text("Value of anotherString is: \(self.myInheritedObject.anotherString)")
}
}
}
}
答案 0 :(得分:6)
iOS 14.5 解决了这个问题。
<块引用>在符合 ObservableObject 的类型的子类中使用 Published 现在可以正确发布更改。 (71816443)
答案 1 :(得分:3)
最后找到了解决此问题的方法/解决方法。如果从子类中删除属性包装器,然后对变量调用基类objectWillChange.send(),则状态将正确更新。
注意::请勿在子类上重新声明let objectWillChange = PassthroughSubject<Void, Never>()
,因为这将再次导致状态无法正确更新。
我希望这是将来的版本中可以解决的问题,因为objectWillChange.send()
是很多需要维护的样板。
这是一个可以正常工作的示例:
import SwiftUI
class MyTestObject: ObservableObject {
@Published var aString: String = ""
}
class MyInheritedObject: MyTestObject {
// Using @Published doesn't work on a subclass
// @Published var anotherString: String = ""
// If you add the following to the subclass updating the state also doesn't work properly
// let objectWillChange = PassthroughSubject<Void, Never>()
// But if you update the value you want to maintain state
// of using the objectWillChange.send() method provided by the
// baseclass the state gets updated properly... Jaayy!
var anotherString: String = "" {
willSet { self.objectWillChange.send() }
}
}
struct MyTestView: View {
@ObservedObject var myTestObject = MyTestObject()
@ObservedObject var myInheritedObject = MyInheritedObject()
var body: some View {
NavigationView {
VStack(alignment: .leading) {
TextField("Update aString", text: self.$myTestObject.aString)
Text("Value of aString is: \(self.myTestObject.aString)")
TextField("Update anotherString", text: self.$myInheritedObject.anotherString)
Text("Value of anotherString is: \(self.myInheritedObject.anotherString)")
}
}
}
}
答案 2 :(得分:2)
这是因为ObservableObject是一个协议,所以您的子类必须符合该协议,而不是您的父类
示例:
class MyTestObject {
@Published var aString: String = ""
}
final class MyInheritedObject: MyTestObject, ObservableObject {
@Published var anotherString: String = ""
}
现在,类和子类的@Published属性将触发视图事件
答案 3 :(得分:0)
当您的课程不是直接ObservableObject
的子类别时,也会发生这种情况:
class YourModel: NSObject, ObservableObject {
@Published var value = false {
willSet {
self.objectWillChange.send()
}
}
}
答案 4 :(得分:0)
我发现的这个问题的最佳解决方案如下:
向 BaseObservableObject
发布者声明 objectWillChange
:
open class BaseObservableObject: ObservableObject {
public let objectWillChange = ObservableObjectPublisher()
}
然后,要在您的子类中触发 objectWillChange
,您必须处理对可观察类和值类型的更改:
class MyState: BaseObservableObject {
var classVar = SomeObservableClass()
var typeVar: Bool = false {
willSet { objectWillChange.send() }
}
var someOtherTypeVar: String = "no observation for this"
var cancellables = Set<AnyCancellable>()
init() {
classVar.objectWillChange // manual observation necessary
.sink(receiveValue: { [weak self] _ in
self?.objectWillChange.send()
})
.store(in: &cancellables)
}
}
然后你可以继续子类化并在需要的地方添加观察:
class SubState: MyState {
var subVar: Bool = false {
willSet { objectWillChange.send() }
}
}
如果该类已经包含 BaseObservableObject
变量,您可以跳过在根父类中继承 @Published
,因为随后会合成发布者。但请注意,如果您从根父类中删除最后的 @Published
值,子类中的所有 objectWillChange.send()
都会静默停止工作。
很遗憾不得不经历这些步骤,因为以后一旦添加了变量,很容易忘记添加观察。希望我们能得到更好的官方修复。
答案 5 :(得分:-1)
根据我的经验,只需将子类objectWillChange
与基类的objectWillChange
链接起来,就像这样:
class GenericViewModel: ObservableObject {
}
class ViewModel: GenericViewModel {
@Published var ...
private var cancellableSet = Set<AnyCancellable>()
override init() {
super.init()
objectWillChange
.sink { super.objectWillChange.send() }
.store(in: &cancellableSet)
}
}