[SwiftUI 100 天] 手动发布 ObservableObject 的变化

1,265 阅读3分钟

译自 www.hackingwithswift.com/books/ios-s…

更多内容,欢迎关注公众号 「Swift花园」

喜欢文章?不如来个 🔺💛➕三连?关注专栏,关注我 🚀🚀🚀

手动发布 ObservableObject 的变化

遵循 ObservableObject 协议的类可以使用 SwiftUI 的 @Published 属性包装器来自动发布属性的变化,以便使用该类的实例的任何视图能够自动重新调用 body 属性,保持界面与数据的一致。多数情况下,这个机制都可以很好 地工作,不过有时候你可能会需要更多的控制,SwiftUI 对此的解决方式是 objectWillChange

每个遵循 ObservableObject 的类都自动获得一个叫 objectWillChange 的属性。它是一个 publisher,也就是说它做的是和 @Published 属性包装器一样的事情:通知正在观察的视图被观察的对象即将有重要的事情发生。正如它的名字所暗示的,这个 publisher 是在我们即将做出改变的时候发出,这能让 SwiftUI 检查 UI 的状态,并未动画化改变做好准备。

为了演示这一点,我们将构建一个会更新自己 10 次的 ObservableObject 子类。你之前已经见过用 DispatchQueue.main.async() 将工作推回主线程的做法,这一次我们要认识一个类似的方法,叫 DispatchQueue.main.asyncAfter()。它能指定附加的闭包在何时运行,也就是说,我们可以要求闭包 “1 秒后运行”,而不是立刻运行。

在测试的例子中, 我们将在一个从 1 到 10 的循环中使用 asyncAfter() 增加一个整数。这个整数会用 @Published 包装,这样它的所有变化都会被发布给观察该对象的视图。

在代码的某个地方添加下面这个类:

class DelayedUpdater: ObservableObject {
    @Published var value = 0

    init() {
        for i in 1...10 {
            DispatchQueue.main.asyncAfter(deadline: .now() + Double(i)) {
                self.value += 1
            }
        }
    }
}

使用这个类只需要在 ContentView 的某个地方用 ObservedObject 注解某个 DelayedUpdate 类型的属性,然后在 body 中显示它的值,如下:

struct ContentView: View {
    @ObservedObject var updater = DelayedUpdater()

    var body: some View {
        Text("Value is: \(updater.value)")
    }
}

运行代码,你会看到数值一直往上增加,知道 10,正如你预期的那样。

现在,移除 @Published,你会看到 UI 不再变化了。虽然幕后的 asyncAfter() 仍然在执行,但由于没有变化通知发出,UI 不再刷新。

我们可以通过手动发送我前面提到的 objectWillChange 来解决。这种方式能让我们在任意时刻发送变化通知,而不用依赖于 @Published 的自动行为。

value 属性改成下面这样:

var value = 0 {
    willSet {
        objectWillChange.send()
    }
}

这样改完你又获得和之前一样的应用行为 —— UI 会计数增加到 10。但这回,我们有机会在 willSet 观察者中添加额外的功能。也许你需要打日志,或者调用另一个方法,又或者你要 clamp 整数,确保它永远不会超出某个范围 —— 一切尽在掌握之中。


我的公众号 这里有Swift及计算机编程的相关文章,以及优秀国外文章翻译,欢迎关注~

Swift花园微信公众号