ProgressView in SwiftUI

swiftui Jul 13, 2024
ProgressView in SwiftUI

Nobody likes to see the loader or the progress bar that appears when the internet is slow or the app is processing data.

Even though we all don't like to see it, making a progress view is inevitable and at some point, you will have to use it in your app. May it be for showing process while you save data to CoreData or to show progress while you are getting data from URLSession.

ProgressView is a SwiftUI view which shows the progress as the task is close to its completion. It is a common practice to show a progress view in apps to indicate the users that the app is not stuck but is taking time to perform the associated task.

In this article, you'll learn the following key concepts about progress view in SwiftUI:

  1. How to implement a ProgressView
  2. Indeterminate ProgressView
  3. Change ProgressView Size
  4. Determinate ProgressView
  5. ProgressView spanning a date range
  6. ProgressView Style
  7. Changing ProgressView Color

How to Implement a SwiftUI ProgressView

In SwiftUI, the basic implementation of a progress view is really straightforward. You simply have to use a built-in component called ProgressView.

struct ContentView: View {
    var body: some View {
        ProgressView()
    }
}

Result:

The above progress view shows a spinner image but don't indicate how much time a task will take to complete or the current progress state. Based on whether the duration of the task is known in advance or not, we can create two types of progress views:

  1. Indeterminate progress view
  2. Determinate progress view

Indeterminate ProgressView

As the name suggests, Indeterminate progress view is used when the exact duration of the task is unknown. For example, while refreshing an app or while an app connects to the internet to load data.

In this case, only a spinning circular image is displayed to the user, also called an activity indicator. So, what you just learned above in the basic implementation of the progress view is a Indeterminate progress view.

Now, if you want to give additional context to the users about the ongoing task, you can also add a description by simply passing a string to it. Else, if you want to create a specific view to add as a description, then you can use the progress view initialiser with a custom label.

struct ContentView: View {
    var body: some View {
        ProgressView("Loading..")
        ProgressView {
            HStack{
                Image(systemName: "hand.raised")
                Text("Please wait..")
            }
        }
    }
}

Result:

Change ProgressView Size

In addition to this, you can also change the size of the progress view to say, small, regular(default) and large, using the controlSize modifier.

struct ContentView: View {
    var body: some View {
        VStack(spacing: 10) {
            ProgressView()
                .controlSize(.small)
            
            ProgressView()
                .controlSize(.regular)
            
            ProgressView()
                .controlSize(.large)
        }
    }   
}

Result:

Determinate ProgressView

Determinate progress view is used when the duration of the task is determined in advance. It helps users better estimate the amount of time a task will take. It can be used while your app performs tasks like installation process, file uploads or during a data backup.

A determinate progress view shows the progress of a task by filling a linear or circular track as the task moves to it completion.

The ProgressView initialiser for this type of progress view can take in 4 parameters:

  1. value - this is a binding to a numeric state variable depicting the amount of task completed to this point. It's value lies between 0.0 nd total
  2. total(optional) - denotes completion of the task with a default value of 1.0
  3. label(optional) - describes the task in progress
  4. currentValueLabel(optional) - defines the current amount of task completed.
@State private var progress: Float = 20

struct ContentView: View {
    var body: some View {
        ProgressView(value: progress, total: 100) {
            Text("Processing..")
        } currentValueLabel: {
            Text("Current progress: \(Int(progress))%")
        }
    }
}

Result:

In the above code, the initial progress starts at 20 and the task will be considered complete when the progress reaches a total of 100. The currentValue label dynamically shows the current progress percentage. You can manually update the progress by setting a timer to increment the progress at specified intervals.

ProgressView spanning a date range

This is one of the great additions to progress view post WWDC'22 where you can have a progress view which updates automatically as per defined interval.

Say, you are building a grocery delivery app which delivers the grocery in 10 minutes. You can easily show a progress view to show the time it'll take to deliver the grocery.

@State private var dateRange = Date()...Date().addingTimeInterval(10*60)

struct ContentView: View {
    var body: some View {
        ProgressView(timerInterval: dateRange, countsDown: true) {
            Text("Grocery reaching your doorstep soon...")
        }
    }
}

Result:

The countDown value decides whether the progress track will get filled or empty as time passes.

ProgressView Style in SwiftUI

There are two types of progress view styles available. One is the circular style which is the default progress view style of indeterminate progress view. The second is linear style which updates according to the value given and is default for determinate progress view.

In case of determinate progress view, you can also switch from linear to circular style by simply using the progressViewStyle modifier.

@State private var progress: Float = 20

struct ContentView: View {
    var body: some View {
        ProgressView(value: progress, total: 100) {
            Text("Processing..")
        } currentValueLabel: {
            Text("Current progress: \(Int(progress))%")
        }
        .progressViewStyle(.circular)
    }
}

Result:

Note:

  1. iOS doesn't support a circular determinate progress view and indeterminate linear progress view.
  2. However macOS supports both. A circular determinate progress view is displayed as a filling circular track as the task moves to it completion and a linear indeterminate progress view presents an animated progress bar to indicate that the app is performing a task.
  3. You can also provide a custom style to the progress view by creating a styling struct which confirms to ProgressViewStyle protocol and then passing it to the progressViewStyle modifier.

Changing ProgressView Color

The color of the progress view can be changed using the tint modifier which gives a specific color to the spinner. Also, label and the background of the progress view can be customised using the foregroundStyle and background modifiers respectively. A border can also be added by giving a color and a width to the border modifier.

@State private var progress: Float = 20

struct ContentView: View {
    var body: some View {
        ProgressView(value: progress, total: 100) {
            Text("Processing..")
        } currentValueLabel: {
            Text("Current progress: \(Int(progress))%")
        }
        .padding()
        .tint(.green)
        .foregroundStyle(.blue)
        .background(Color.gray.opacity(0.25))
        .border(.yellow, width: 2)
    }
}

Result:

You can also apply the same to the circular progress view.

Result:

Congratulations! You deserve to celebrate this moment. Today you learned about Progress View in SwiftUI, it's types, how to show a progress bar in a couple of ways and how to style it's appearance. If you are looking to build a custom progress bar, you can check out How to create circular progress bar in SwiftUI.


We at Swift Anytime have the mission to make learn iOS development the way everyone enjoys it. You can check out our articles on SwiftUI, Swift, iOS Interview Questions and get started with your iOS journey today.​​

Signup now to get notified about our
FREE iOS Workshops!