top of page
Writer's pictureHui Wang

35. SwiftUI: Using UIKit's UIActivityIndicatorView in SwiftUI

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


bottom of page