Today I will show you how to use AnyCancellable to hide UIActivityIndicatorView.
Steps:
Continue the source code of the previous blog (blog 35).
First, import the Combine framework.
Then declare a class. This class conforms to the ObservableObject protocol. @ObservableObject and @State property wrappers are very similar, the difference is that ObservableObject is for objects.
import Combine
class GlobalData: ObservableObject {
}
Add a @Published property wrapper, which is often used with ObservableObject. It allows properties in the observable to be listened to, thus serving a similar function to @State.
@Published var isActive = false
Add an AnyCancellable object. AnyCancellable is a cancelable object of type erasure that executes the provided closure when canceled. For example, we are using this property today to time hide UIActivityIndicatorView.
var cancellable: AnyCancellable? = nil
Add an init() method to initialize the property.
Pause for 2 seconds, then perform the following actions. It is published by an upstream publisher on the main thread, which publishes the latest or first element.
After 2 seconds, send a message with the content false. Transforms all elements of the upstream publisher using the AnyCancellable closure.
Finally, the false processed by the map is published to the isActive property of self, thereby modifying the value of this property to false to hide UIActivityIndicatorView.
init() {
cancellable = $isActive
.delay(for: 3, scheduler: RunLoop.main)
.map {val in false}
.assign(to: \.isActive, on: self)
}
Declare a property to receive the passed data. This property has a @EnvironmentObject property wrapper.
Display and hide UIActivityIndicatorView according to the value of the Bool property of EnvironmentObject.
@EnvironmentObject var globalData: GlobalData
var body: some View {
LoadingView(isActive: $globalData.isActive)
}
To use the ObservableObject in the preview window, we need to initialize the ObservableObject object in the PreviewProvider.
Then pass the ObservableObject object as EnvironmentObject to the instance of ContentView.
let globalData = GlobalData()
globalData.isActive = true
return ContentView().environmentObject(globalData)
Source Code:
import SwiftUI
import UIKit
import Combine
class GlobalData: ObservableObject {
@Published var isActive = false
var cancellable: AnyCancellable? = nil
init() {
cancellable = $isActive
.delay(for: 3, scheduler: RunLoop.main)
.map {val in false}
.assign(to: \.isActive, on: self)
}
}
struct ContentView: View {
@EnvironmentObject var globalData: GlobalData
var body: some View {
LoadingView(isActive: $globalData.isActive)
}
}
struct ActivityIndicator: UIViewRepresentable {
@Binding var isActive: Bool
func makeUIView(context: UIViewRepresentableContext<ActivityIndicator>) -> UIActivityIndicatorView {
UIActivityIndicatorView(style: .large)
}
func updateUIView(_ uiView: UIActivityIndicatorView, context: UIViewRepresentableContext<ActivityIndicator>) {
isActive ? uiView.startAnimating() : uiView.stopAnimating()
}
}
struct LoadingView: View {
@Binding var isActive: Bool
var body: some View {
VStack {
Text("Loading...")
ActivityIndicator(isActive: $isActive)
}
.frame(width: 180, height: 180)
.background(.teal)
.foregroundColor(.primary)
.cornerRadius(20)
.opacity(isActive ? 1 : 0)
}
}
#if DEBUG
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
let globalData = GlobalData()
globalData.isActive = true
return ContentView().environmentObject(globalData)
}
}
#endif
Follow me on:
Comentarios