@Published 属性 包装器不适用于 ObservableObject 的子类
@Published property wrapper not working on subclass of ObservableObject
我有一个符合@ObservableObject 协议的class,并从它创建了一个子class,它带有自己的变量,带有@Published 属性 包装器来管理状态。
似乎在使用子class 时忽略了@published 属性 包装器。有谁知道这是否是预期的行为,是否有解决方法?
我是 运行 iOS 13 Beta 8 和 xCode Beta 6。
这是我所看到的示例。在 MyTestObject
上更新 TextField 时,Text 视图会使用 aString 值正确更新。如果我更新 MyInheritedObject
TextField,文本视图中的 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)")
}
}
}
}
终于解决了这个问题solution/workaround。如果从子类中删除 属性 包装器,并在变量上调用基类 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)")
}
}
}
}
当您的 class 不是直接子 class ObservableObject
:
时也会发生这种情况
class YourModel: NSObject, ObservableObject {
@Published var value = false {
willSet {
self.objectWillChange.send()
}
}
}
这是因为 ObservableObject 是一个协议,所以你的子class必须符合协议,而不是你的父class
示例:
class MyTestObject {
@Published var aString: String = ""
}
final class MyInheritedObject: MyTestObject, ObservableObject {
@Published var anotherString: String = ""
}
现在,class 和 subclass 的 @Published 属性将触发视图事件
根据我的经验,只需将子 class objectWillChange
与基础 class 的 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)
}
}
更新
这已在 iOS 14.5 和 macOS 11.3 中修复,ObservableObject 的子classes 将正确发布这些版本的更改。但请注意,当用户在任何较旧的次要 OS 版本上 运行 时,同一应用程序将出现原始问题。对于这些版本中使用的任何 class,您仍然需要以下解决方法。
我找到的解决这个问题的最佳方法如下:
与 objectWillChange
发布者声明 BaseObservableObject
:
open class BaseObservableObject: ObservableObject {
public let objectWillChange = ObservableObjectPublisher()
}
然后,要在子 class 中触发 objectWillChange
,您必须处理对可观察 classes 和值类型的更改:
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)
}
}
然后你可以继续 subclassing 并在需要的地方添加观察:
class SubState: MyState {
var subVar: Bool = false {
willSet { objectWillChange.send() }
}
}
如果 class 已经包含 @Published
变量,您可以跳过在根父 class 中继承 BaseObservableObject
,因为发布者随后被合成。但要小心,如果你从根父 class 中删除最终的 @Published
值,子 class 中的所有 objectWillChange.send()
将默默地停止工作。
非常不幸的是必须经历这些步骤,因为一旦你以后添加一个变量,很容易忘记添加观察。希望我们能得到更好的官方修复。
iOS 14.5 解决了这个问题。
Combine
Resolved Issues
Using Published in a subclass of a type conforming to ObservableObject now correctly publishes changes. (71816443)
我有一个符合@ObservableObject 协议的class,并从它创建了一个子class,它带有自己的变量,带有@Published 属性 包装器来管理状态。
似乎在使用子class 时忽略了@published 属性 包装器。有谁知道这是否是预期的行为,是否有解决方法?
我是 运行 iOS 13 Beta 8 和 xCode Beta 6。
这是我所看到的示例。在 MyTestObject
上更新 TextField 时,Text 视图会使用 aString 值正确更新。如果我更新 MyInheritedObject
TextField,文本视图中的 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)")
}
}
}
}
终于解决了这个问题solution/workaround。如果从子类中删除 属性 包装器,并在变量上调用基类 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)")
}
}
}
}
当您的 class 不是直接子 class ObservableObject
:
class YourModel: NSObject, ObservableObject {
@Published var value = false {
willSet {
self.objectWillChange.send()
}
}
}
这是因为 ObservableObject 是一个协议,所以你的子class必须符合协议,而不是你的父class
示例:
class MyTestObject {
@Published var aString: String = ""
}
final class MyInheritedObject: MyTestObject, ObservableObject {
@Published var anotherString: String = ""
}
现在,class 和 subclass 的 @Published 属性将触发视图事件
根据我的经验,只需将子 class objectWillChange
与基础 class 的 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)
}
}
更新
这已在 iOS 14.5 和 macOS 11.3 中修复,ObservableObject 的子classes 将正确发布这些版本的更改。但请注意,当用户在任何较旧的次要 OS 版本上 运行 时,同一应用程序将出现原始问题。对于这些版本中使用的任何 class,您仍然需要以下解决方法。
我找到的解决这个问题的最佳方法如下:
与 objectWillChange
发布者声明 BaseObservableObject
:
open class BaseObservableObject: ObservableObject {
public let objectWillChange = ObservableObjectPublisher()
}
然后,要在子 class 中触发 objectWillChange
,您必须处理对可观察 classes 和值类型的更改:
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)
}
}
然后你可以继续 subclassing 并在需要的地方添加观察:
class SubState: MyState {
var subVar: Bool = false {
willSet { objectWillChange.send() }
}
}
如果 class 已经包含 @Published
变量,您可以跳过在根父 class 中继承 BaseObservableObject
,因为发布者随后被合成。但要小心,如果你从根父 class 中删除最终的 @Published
值,子 class 中的所有 objectWillChange.send()
将默默地停止工作。
非常不幸的是必须经历这些步骤,因为一旦你以后添加一个变量,很容易忘记添加观察。希望我们能得到更好的官方修复。
iOS 14.5 解决了这个问题。
Combine
Resolved Issues
Using Published in a subclass of a type conforming to ObservableObject now correctly publishes changes. (71816443)