SwiftUI提供了多种方法将给定视图连接到它所依赖的底层状态,例如使用@State和@ObservedObject这样的属性包装器。虽然在绝大多数情况下,使用这些属性包装器当然是首选的方法,但另一个值得记住的选择是,我们也可以直接在我们的SwiftUI视图中观察组合发布者
作为一个例子,让我们说我们在一个视图上工作,只要应用程序留在前台,就会播放一个重复的动画,我们想暂停那个动画,当那不再是情况。
为了做到这一点,我们可以使用NotificationCenter,它(因为iOS 13 + macOS Catalina)附带了一个组合驱动的API,让我们可以轻松地为我们想要观察的任何通知创建发布者。然后,我们可以使用onReceive修饰符将发布者连接到我们的swifttui视图的主体上,就像这样:
struct AnimationView: View {
@State private var isAnimating = true
var body: some View {
VStack {
...
}
.onReceive(NotificationCenter.default.publisher(
for: UIApplication.didEnterBackgroundNotification
)) { _ in
// The app moved to the background
isAnimating = false
}
}
}
请注意,我们不需要让视图存储为AnyCancellable,也不需要以任何方式管理到发布者的连接——swift为我们处理所有这些!
然而,观察NotificationCenter-provided出版商所需的代码相当冗长,如果上面是一个模式,我们希望跟随在多个地方在我们的代码库,然后我们还可以实现一些方便的api,会让我们更容易这样做:
extension View {
func onNotification(
_ notificationName: Notification.Name,
perform action: @escaping () -> Void
) -> some View {
onReceive(NotificationCenter.default.publisher(
for: notificationName
)) { _ in
action()
}
}
func onAppEnteredBackground(
perform action: @escaping () -> Void
) -> some View {
onNotification(
UIApplication.didEnterBackgroundNotification,
perform: action
)
}
}
有了上面的内容,我们现在可以通过这样做来观察当我们的应用移动到背景时:
struct AnimationView: View {
@State private var isAnimating = true
var body: some View {
VStack {
...
}
.onAppEnteredBackground {
isAnimating = false
}
}
}
最后,使用onReceive修饰符也是观察我们自己的自定义发布者的好方法——在设置对可变状态块的引用时,它可以作为像ObservedObject这样的更轻量级的替代。
例如,ItemList使用发布者来观察项目值的外部数组,然后将其存储在本地@State属性中,以连接到它的主体:
struct ItemList: View {
var publisher: AnyPublisher<[Item], Never>
@State private var items = [Item]()
var body: some View {
List(items) { item in
ItemRow(item: item)
}
.onReceive(publisher) {
items = $0
}
}
}
当然,这并不意味着我们应该用上面的模式来替换ObservedObject的所有用法,但是当我们只想观察单个事件而不是一个复杂的对象时,这是一种很好的技术。