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的所有用法,但是当我们只想观察单个事件而不是一个复杂的对象时,这是一种很好的技术。

原文链接