假设我有以下ObservableObject
:
import SwiftUI
class SomeObservable: ObservableObject {
@Published var information: String = ""
init() {
Timer.scheduledTimer(
timeInterval: 1.0,
target: self,
selector: #selector(updateInformation),
userInfo: nil,
repeats: true
).fire()
}
@objc func updateInformation() {
information = String("RANDOM_INFO".shuffled().prefix(5))
}
}
还有一个View
,它会观察到:
struct SomeView: View {
@ObservedObject var observable: SomeObservable
var body: some View {
Text(observable.information)
}
}
以上将按预期工作。
View
发生更改时,ObservableObject
会重新绘制自身:
如何在同样观察到struct
的“纯” ObservableObject
中执行相同的操作(例如调用函数)? “纯”是指不符合的View
:
struct SomeStruct {
@ObservedObject var observable: SomeObservable
// How to call this function when "observable" changes?
func doSomethingWhenObservableChanges() {
print("Triggered!")
}
}
(也可以是class
,只要它能够对可观察对象的变化做出反应。)
从概念上讲,这似乎很简单,但是我显然缺少了一些东西。
(注意:我正在使用Xcode 11,测试版6。)
根据@Fabian提供的出色答案,这是一个可能的解决方案:
import SwiftUI
import Combine
class SomeObservable: ObservableObject {
@Published var information: String = "" // Will be automagically consumed by `Views`.
let updatePublisher = PassthroughSubject<Void, Never>() // Can be consumed by other classes / objects.
// Added here only to test the whole thing.
var someObserverClass: SomeObserverClass?
init() {
// Randomly change the information each second.
Timer.scheduledTimer(
timeInterval: 1.0,
target: self,
selector: #selector(updateInformation),
userInfo: nil,
repeats: true
).fire() }
@objc func updateInformation() {
// For testing purposes only.
if someObserverClass == nil { someObserverClass = SomeObserverClass(observable: self) }
// `Views` will detect this right away.
information = String("RANDOM_INFO".shuffled().prefix(5))
// "Manually" sending updates, so other classes / objects can be notified.
updatePublisher.send()
}
}
struct SomeObserverView: View {
@ObservedObject var observable: SomeObservable
var body: some View {
Text(observable.information)
}
}
class SomeObserverClass {
@ObservedObject var observable: SomeObservable
// More on AnyCancellable on: apple-reference-documentation://hs-NDfw7su
var cancellable: AnyCancellable?
init(observable: SomeObservable) {
self.observable = observable
// `sink`: Attaches a subscriber with closure-based behavior.
cancellable = observable.updatePublisher.sink(receiveValue: { [weak self] _ in
guard let self = self else { return }
self.doSomethingWhenObservableChanges()
})
}
func doSomethingWhenObservableChanges() {
print(observable.information)
}
}
结果
(注意:必须运行该应用程序才能检查控制台输出。)
答案 0 :(得分:5)
旧方法是使用您注册的回调。较新的方法是使用Combine
框架创建可以对其进行进一步处理的发布者,或者在这种情况下,sink
每次source publisher
发送消息时都会被调用。此处的发布者不发送任何内容,因此类型为<Void, Never>
。
要通过计时器获取发布者,可以直接通过Combine
完成,也可以通过PassthroughSubject<Void, Never>()
创建通用发布者,注册消息并通过{{1}在timer-callback
中发送消息}。该示例有两种变体。
每个publisher.send()
都有一个ObservableObject
发布者,您可以像为.objectWillChange
一样向其注册sink
。每次您调用它或每次Timer publishers
变量更改时都应该调用它。但是请注意,这是在更改之前而不是更改之后被调用的。 (更改完成后,接收器内的@Published
会做出反应)。
每个接收器调用都会创建一个DispatchQueue.main.async{}
,必须将其存储在通常与AnyCancellable
应该具有相同生存期的对象中。一旦取消可取消结构(或在其上调用sink
),就不会再次调用.cancel()
。
sink