符合ObservableObject
协议的类可以使用SwiftUI的@Published
属性包装器自动声明对属性的更改,以便使用该对象的所有视图都可以重新调用其body
属性,并与数据保持同步。在很多时候,这确实非常有效,但是有时您需要更多的控制权,而SwiftUI的解决方案称为objectWillChange
。
每个符合ObservableObject
的类都会自动获得一个名为objectWillChange
的属性。这是一个发布者,这意味着它执行与@Published
属性包装器相同的工作:它通知正在观察该对象的所有视图一些重要的更改。顾名思义,此发布者应在我们进行更改之前立即触发,这使SwiftUI可以检查UI的状态并为动画更改做准备。
为了演示这一点,我们将构建一个可更新10次的ObservableObject
类。您已经认识到DispatchQueue.main.async()
是将工作推送到主线程的一种方式,但是在这里,我们将使用一种称为DispatchQueue.main.asyncAfter()
的类似方法。这使我们可以指定何时运行附加的闭包,这意味着我们可以说“在1秒钟后执行此工作”,而不是“立即执行此工作”。
在此测试用例中,我们将在1到10的循环内使用asyncAfter()
,因此我们将整数增加10个值。该整数将使用@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
属性,然后在我们的主体中显示该值,如下所示:
struct ContentView: View {
@ObservedObject var updater = DelayedUpdater()
var body: some View {
Text("Value is: \(updater.value)")
}
}
运行该代码时,您会看到该值一直递增,直到达到10,这正是您所期望的。
现在,如果删除@Published
属性包装,您将看到UI不再更改。在后台,所有的asyncAfter()
工作仍在进行,但不会导致UI刷新,因为没有发出更改通知。
尝试将value
属性更改成如下内容:
var value = 0 {
willSet {
objectWillChange.send()
}
}
![](https://img.haomeiwen.com/i2955252/d8bd365a89ba8752.png)
现在,您将再次恢复原来的行为——用户界面将像以前一样计数为10。除了这次,我们有机会在willSet
观察器中添加额外的功能。也许您想要记录一些东西,也许您想要调用另一个方法,或者您想要钳制整数内部值,以使它永远不会超出范围——现在所有这些都在我们的控制之下。
网友评论