Progress View in SwiftUI

Progress View in SwiftUI

Last updated on August 19th, 2022

⏱ Reading Time: 6 mins

The progress view in SwiftUI is a native view that was introduced in WWDC 2020 and makes it really easy to indicate visually the progress of long-running tasks that take time to complete.

There are two kind of tasks to report progress for:

  • Those whose completion time can be determined.
  • Those whose completion time is impossible to predict.

For that reason, progress view exists in two types respectively; the linear and the indeterminate. The first one is a bar that gets filled according to the task’s completion amount, while the second is by default a circular activity indicator spinning around indefinitely until the task is over.

We are going to see both in this post, starting with the indeterminate type of progress view. However, the demonstration will not stop in the built-in progress view styles only. We will also get to know how to create custom progress view styles in order to implement unique progress views.

The indeterminate progress view

The simplest way to present an indeterminate progress view (a spinner) is this:

The above will display the default spinner that we are familiar with. A message can easily be presented along with the spinner; we simply need to provide it as argument to the ProgressView:

Indeterminate progress view with message

We may want sometimes to change the default tint color of the progress view. The way to manage that depends on the target operating systems that the app is going to be available for. For instance:

  • If running in iOS 15 and above, then we set a tint color by applying the tint(_:) view modifier to the progress view.
  • For previous system versions, we have to use the progressViewStyle(_:) modifier passing as argument a CircularProgressViewStyle instance. Then we provide the tint color as argument to the latter.

To handle each case easily, we can use an if #available statement as shown next. Note that this wouldn’t be necessary if there is no need to support older system versions.

Indeterminate progress view with tint color

To change the label’s color as well, we can use the foregroundColor modifier:

Besides all the above that demonstrate the most common kind of usage of the indeterminate progress view, it’s also possible to provide and display any other SwiftUI view instead of just a text as argument. The following creates a progress view that contains a button that’s supposed to stop the task. That button is now the progress view’s label:

Indeterminate progress view with custom button as label

The linear progress view

In order to indicate progress using the linear progress view style, we must necessarily provide it with two values:

  • The final value that indicates the task completion.
  • The current progress value towards the completion.

In its simplest form, a linear progress view can be presented like so:

The progress value indicates the current progress, and usually it’s a @State property:

Similarly to the indeterminate progress view, we can provide a text label here too:

Linear progress view

Of course, the above is not enough in order to see the progress view animating; we have to make sure that the progress value gets updated somehow.

The color of the progress bar can be changed, but similarly to the previous progress style, the way to achieve it depends on the system version. For iOS 15 and above we have to use the tint(_:) modifier, but for earlier versions the accentColor(_:) modifier is what we have to apply.

Note that only the progress bar’s color is changed with the view modifiers that were just mentioned. In order to change the label’s color there is another view modifier that has to be used; the foregroundColor(_:).

Once again, we can use an if #available statement so as to use the proper modifiers depending on the system version. We can avoid that if iOS 15 is the minimum required version for the app:

Linear progress view with tint color

Custom progress view styles

The default appearance of the progress view can be overridden by implementing custom progress view styles. Such a style is actually a custom type, a structure, that conforms to the ProgressViewStyle protocol. The only requirement is to implement a method called makeBody(configuration:) and return either a progress view, or any other SwiftUI view customized the way we want it.

The following snippet presents a custom progress view style. All it does is to add a background color to the progress bar, change the default tint color, add some padding and rounded corners:

See that a ProgressView instance is initialized with the configuration parameter value, it gets the style we desire by applying various view modifiers, and eventually it’s returned from the method. To use it, we just create a new progress view that will be using this style with the help of the progressViewStyle view modifier:

Linear progress view with custom style and background color

I mentioned previously that the return value of the makeBody(configuration:) method can be any SwiftUI view. With that in mind, you can see right next the implementation of another custom style. The view we return this time is a ZStack with two overlapping rounded rectangles. The first one is the background of the progress view, the other indicates the current progress that is used to increase the width accordingly:

Using the above once again with the progressViewStyle modifier:

The result is this:

Linear progress view with custom style and two rounded rectangle views

A linear progress view does not have to be always horizontal. To demonstrate that, here is one last custom style implementation that’s a bit different from the above two:

The return value is once again a ZStack. We have a circular shape whose stroke is trimmed according to the current progress value; the higher the progress value, the more the circle is completed with a stroke.

The second shape is a text, “sitting” at the center of the circle and showing the progress percentage, or the “Done” value on completion.

See that in order to get the current progress we are using the fractionCompleted property from the configuration object. It’s value is in the range 0…1, but for indeterminate progress views it becomes nil. That’s why it’s necessary to unwrap its value before using it in the Text view right above.

Using this custom style is similar to the previous cases:

And the result is this:

Linear progress view with custom style and filling circle with progress text

Summary

Presenting a progress view in SwiftUI is easy, especially if we want to stick to the system provided ones. We’ve met various options to customize it with view modifiers, but for unique progress views, implementing custom styles is the only way to go. You can unleash your imagination and create intuitive progress views that’d be the perfect match for the user interface of your apps. I presented a few simple styles in this post, but you can go as far as you want with it; it’s always up to you. In any case, I hope what you read here today to be proved helpful in your projects.

Thanks for reading! 🧑‍💻

If you found this post useful then please consider sharing it! Also, subscribe to my newsletter in order to be notified about everything new published here directly in your inbox, and follow me on Twitter, on YouTube, on Medium and other social media.

Stay Up To Date

Subscribe to my newsletter and get notifiied instantly when I post something new on SerialCoder.dev.

    We respect your privacy. Unsubscribe at any time.