Translation of www.hackingwithswift.com/books/ios-s…
For more content, please follow the public account “Swift Garden”.
Like articles? How about πΊπβ company 3? Follow this column, follow me πππ
Manually publish changes to ObservableObject
Classes that follow the ObservableObject protocol can use SwiftUI’s @Published property wrapper to automatically publish property changes so that any view that uses an instance of the class can automatically re-invoke the Body property, keeping the interface consistent with the data. Most of the time this works fine, but there may be times when you need more control and SwiftUI’s solution to this is objectWillChange.
Every class that obeys ObservableObject automatically acquires a property called objectWillChange. It is publisher, which means it does the same thing as the @Published property wrapper: notifies the viewing view that something important is about to happen to the observed object. As the name suggests, this publisher was sent when we were about to make a change, which allowed SwiftUI to check the state of the UI before animating the change.
To demonstrate this, we will build an ObservableObject subclass that will update itself 10 times. You have seen before use DispatchQueue. Main. Async () will work back into the practice of the main thread, this time we want to know a similar approach, called DispatchQueue. Main. AsyncAfter (). It can specify when the additional closure will run, that is, we can ask the closure to “run in 1 second” instead of running right away.
In the test example, we will increment an integer in a loop from 1 to 10 using asyncAfter(). The integer is wrapped with @Published so that all changes to it are Published to the view observing the object.
Add the following class somewhere in the code:
class DelayedUpdater: ObservableObject {@Published var value = 0
init() {
for i in 1.10 {
DispatchQueue.main.asyncAfter(deadline: .now() + Double(i)) {
self.value += 1}}}}Copy the code
To use this class, just annotate a property of type DelayedUpdate with ObservedObject somewhere in the ContentView and display its value in the body as follows:
struct ContentView: View {@ObservedObject var updater = DelayedUpdater(a)var body: some View {
Text("Value is: \(updater.value)")}}Copy the code
Run the code, and you’ll see the value keep going up until 10, just as you’d expect.
Now, remove @Published, and you’ll see that the UI is no longer changing. While asyncAfter() behind the scenes is still executing, the UI is no longer refreshed because no change notifications are issued.
We can fix this by manually sending the objectWillChange I mentioned earlier. This allows us to send change notifications at any time without relying on the automated behavior of @published.
Change the value attribute to the following:
var value = 0 {
willSet {
objectWillChange.send()
}
}
Copy the code
You get the same behavior as before — the UI accounting goes up to 10. But this time, we have the opportunity to add additional functionality to the willSet observer. Maybe you need to log, or call another method, or maybe you have to clamp integers to make sure it never gets out of a certain range — everything is under control.
My official account here Swift and computer programming related articles, as well as excellent translation of foreign articles, welcome to pay attention to ~