In addition to the circular progress bar provided by SwiftUI 2.0, we can also use UIActivityIndicatorView in UIKit to achieve this.
Steps:
First, add a new struct that conforms to UIViewRepresentable.
When passing property values between structs, we need to use the @Binding property wrapper to turn the property into a reference type to bind the properties between structs by reference.
When the value of property changes, the property of another struct that references the property also changes simultaneously.
struct ActivityIndicator: UIViewRepresentable {
@Binding var isActive: Bool
}
Implement the makeUIView method to initialize and return a UIActivityIndicatorView and set its style to a large size.
func makeUIView(context: UIViewRepresentableContext<ActivityIndicator>) -> UIActivityIndicatorView {
UIActivityIndicatorView(style: .large)
}
Next, implement the updateUIView method in the protocol.
Sets UIActivityIndicatorView's running state based on the boolean property.
func updateUIView(_ uiView: UIActivityIndicatorView, context: UIViewRepresentableContext<ActivityIndicator>) {
isActive ? uiView.startAnimating() : uiView.stopAnimating()
}
Add a struct object that conforms to the View protocol to create a UIActivityIndicatorView with hint text.
Also, use the @Binding property wrapper to add a property that identifies whether to start animating the UIActivityIndicatorView.
struct LoadingView: View {
@Binding var isActive: Bool
}
Implement the mandatory body property in the protocol, and all subviews need to be placed here.
Add a VStack as a container for subviews.
Then add a Text and ActivityIndicator, Text as the label of ActivityIndicator.
Set the width and height of the VStack.
Then set the background, foregroundColor, and cornerRadius of the VStack.
Continue to set the opacity of VStack. When the boolean value of the property is false, hide the ActivityIndicator.
var body: some View {
VStack {
Text("Loading...")
ActivityIndicator(isActive: $isActive)
}
.frame(width: 200, height: 200)
.background(.blue)
.foregroundColor(.primary)
.cornerRadius(20)
.opacity(isActive ? 1 : 0)
}
Add a Bool property that indicates whether to perform the UIActivityIndicatorView animation.
Add a UIActivityIndicatorView and pass a boolean property.
Since the properties of UIActivityIndicatorView have @Binding property wrappers, when the value of the isActive property changes, the value of the properties in UIActivityIndicatorView will also change.
@State var isActive = true
var body: some View {
LoadingView(isActive: $isActive)
}
Add an onAppear method to execute a piece of code after the UIActivityIndicatorView is displayed.
Then create a Timer. The interval is 2 seconds, and it only executes once.
After the UIActivityIndicatorView is displayed for 2 seconds, set the property's value to false, which means hide the UIActivityIndicatorView, and cancel the Timer.
.onAppear {
Timer.scheduledTimer(withTimeInterval: 2, repeats: false) { timer in
isActive.toggle()
timer.invalidate()
}
}
Source Code:
import SwiftUI
import UIKit
struct ContentView: View {
@State var isActive = true
var body: some View {
LoadingView(isActive: $isActive)
.onAppear {
Timer.scheduledTimer(withTimeInterval: 2, repeats: false) { timer in
isActive.toggle()
timer.invalidate()
}
}
}
}
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)
}
}
Follow me on:
Comments