我发现了很多与SwiftUI相关的主题,但这些都没有帮助(比如Why an ObservedObject array is not updated in my SwiftUI application?)
这不适用于Swift中的组合(特别是而不是使用SwiftUI的):
class SomeTask {
@Published var progress = Progress(totalUnitCount: 5) // Progress is a Class
[...]
}
var task = SomeTask()
let cancellable = task.$progress.sink { print($0.fractionCompleted) }
task.progress.completedUnitCount = 2
这与SwiftUI无关,所以没有ObservableObject
继承来获取objectWillChange
,但是即使我尝试使用ObservableObject
和task.objectWillChange.send()
,它也什么也不做,也尝试添加extension Progress: ObservableObject {}
也没有帮助。由于发行者通过var的willSet
发出值,并且由于Progress
本身是类类型,所以不会发生任何事情。
似乎没有真正合适的方法来手动触发它?
只有我找到的解决方案是重新分配自己,这是相当尴尬的:
let pr = progress progress = pr
(编写progress = progress
是编译时错误)。
唯一可行的方法可能是使用Key-value-observing/KVO和/或编写一个新的@PublishedClassType
属性包装器?
发布于 2020-06-06 08:12:01
我能够使用由@propertyWrapper
包装的KVO实现这一功能,CurrentValueSubject
作为发布服务器:
@propertyWrapper
class PublishedClass<T : NSObject> {
private let subject: CurrentValueSubject<T, Never>
private var observation: NSKeyValueObservation? = nil
init<U>(wrappedValue: T, keyPath: ReferenceWritableKeyPath<T, U>) {
self.wrappedValue = wrappedValue
subject = CurrentValueSubject(wrappedValue)
observation = wrappedValue.observe(keyPath, options: [.new]) { (wrapped, change) in
self.subject.send(wrapped)
}
}
var wrappedValue: T
var projectedValue: CurrentValueSubject<T, Never> {
subject
}
deinit {
observation.invalidate()
}
}
用法:
class Bar : NSObject {
@objc dynamic var a: Int
init(a: Int) {
self.a = a
}
}
class Foo {
@PublishedClass(keyPath: \.a)
var bar = Bar(a: 0)
}
let f = Foo()
let c = f.$bar.sink(receiveValue: { x in print(x.a) })
f.bar.a = 2
f.bar.a = 3
f.bar.a = 4
输出:
0
2
3
4
当然,使用KVO的缺点是您传入的关键路径必须是@objc dynamic
,而键盘的根必须是NSObject
子类。:(
我还没有尝试过,但是如果需要的话,应该可以将其扩展到多个关键路径上观察。
发布于 2020-06-05 11:20:52
您可以尝试使用CurrentValueSubject<Progress, Never>
class SomeTask: ObservableObject {
var progress = CurrentValueSubject<Progress, Never>(Progress(totalUnitCount: 5))
func setProgress(_ value: Int) {
progress.value.completedUnitCount = value
progress.send(progress.value)
}
}
var task = SomeTask()
let cancellable = task.progress.sink { print($0.fractionCompleted) }
task.setProgress(3)
task.setProgress(1)
这样,您的Progress
仍然可以是一个类。
发布于 2020-06-06 08:59:06
基于这些想法,我确实实现了一个@PublishedKVO
属性包装器,并将它作为一个支持多个密钥路径的小型快捷包放在github上。
https://github.com/matis-schotte/PublishedKVO
可用于:
class Example {
@PublishedKVO(\.completedUnitCount)
var progress = Progress(totalUnitCount: 2)
@Published
var textualRepresentation = "text"
}
let ex = Example()
// Set up the publishers
let c1 = ex.$progress.sink { print("\($0.fractionCompleted) completed") }
let c1 = ex.$textualRepresentation.sink { print("\($0)") }
// Interact with the class as usual
ex.progress.completedUnitCount += 1
// outputs "0.5 completed"
// And compare with Combines @Published (almost°) same behaviour
ex.textualRepresentation = "string"
// outputs "string"
ex.$progress.emit() // Re-emits the current value
ex.$progress.send(ex.progress) // Emits given value
https://stackoverflow.com/questions/62212563
复制相似问题