Reading time: 1 min

This recipe shows how to render a progress dialog / HUD in SwiftUI.

The end result looks like this:

This component is available as a Swift Package in this repo.

In order to make this happen, we'll use the custom view dialog from this recipe. If you haven't yet, be sure to check it out!

OK, here's the code:

extension View {
  func progressDialog(isShowing: Binding<Bool>, message: String) -> some View {
    self.genericDialog(isShowing: isShowing, cancelOnTapOutside: false) {
      HStack(spacing: 10) {
        if #available(iOS 14.0, *) {
          ProgressView()
            .progressViewStyle(CircularProgressViewStyle())
        } else {
          ActivityIndicator(isAnimating: true)
        }
        Text(message).bodyText
      }.padding()
    }
  }
}

Then, you can use it like any other dialog:

struct MyView: View {
  @State private var isLoading = false

  var body: some View {
    VStack {
      Text("Content")
    }.progressDialog(isShowing: $isLoading, message: "Loading...")
  }
}

The ActivityIndicator is an iOS 13-compatible replacement for circular ProgressView, and here is its code:

struct ActivityIndicator: UIViewRepresentable {
  public typealias UIView = UIActivityIndicatorView
  public var isAnimating: Bool = true
  public var configuration = { (indicator: UIView) in }

  public init(isAnimating: Bool, configuration: ((UIView) -> Void)? = nil) {
    self.isAnimating = isAnimating
    if let configuration = configuration {
       self.configuration = configuration
    }
  }

  public func makeUIView(context: UIViewRepresentableContext<Self>) -> UIView {
    UIView()      
  }

  public func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<Self>) {
    isAnimating ? uiView.startAnimating() : uiView.stopAnimating()
    configuration(uiView)
  }
}

extension View where Self == ActivityIndicator {
  func configure(_ configuration: @escaping (Self.UIView) -> Void) -> Self {
    Self.init(isAnimating: self.isAnimating, configuration: configuration)
  }
}

Next Post Previous Post