Skip to content

Creating a Circular Progress Bar in SwiftUI

SwiftUI, Circular Progress Bar, UI Design2 min read

When it comes to designing user interfaces for iOS apps, progress bars are commonly used to indicate the completion status of a task or process. While UIKit provides a built-in linear progress bar, SwiftUI doesn't have a native circular progress bar component. In this article, we'll explore how to create a custom circular progress bar in SwiftUI using the power of shapes and animations.


This tutorial assumes you have basic knowledge of SwiftUI and Swift programming. If you're new to SwiftUI, it's recommended to familiarize yourself with the basics before diving into this implementation.

Getting Started

To create a circular progress bar, we'll leverage the Shape protocol in SwiftUI to define our custom shape. We can start by creating a new SwiftUI view called CircularProgressBarView.

1import SwiftUI
3struct CircularProgressBarView: View {
4 private let progress: Double
6 init(progress: Double) {
7 self.progress = progress
8 }
10 var body: some View {
11 ZStack {
12 Circle()
13 .stroke(Color.gray.opacity(0.2), style: StrokeStyle(lineWidth: 10))
15 Circle()
16 .trim(from: 0, to: CGFloat(min(self.progress, 1.0)))
17 .stroke(, style: StrokeStyle(lineWidth: 10, lineCap: .round))
18 .rotationEffect(Angle(degrees: -90))
19 .animation(.linear, value: progress)
21 Text("\(Int(progress * 100))%")
22 .font(.title)
23 }
24 }

In the above code snippet, we define the CircularProgressBarView as a struct. It takes a progress value as input, which represents the completion percentage of the progress bar. The view consists of a ZStack to layer the components over each other.

Inside the ZStack, we create two circles using the Circle shape. The first circle acts as the background stroke with a gray color and opacity. The second circle is the actual progress indicator that trims based on the provided progress value. We use the trim modifier to control the start and end points of the progress bar.

To make the progress bar animate smoothly, we apply the .rotationEffect modifier with an angle of -90 degrees to align the starting point at the top. Additionally, we include the .animation modifier with a linear animation style to achieve a smooth transition when updating the progress.

Finally, we add a Text view to display the progress percentage inside the circular progress bar.

Using the Circular Progress Bar

Now that we have our custom circular progress bar view, we can integrate it into our SwiftUI project. Here's an example of how you can use it:

1struct ContentView: View {
2 @State private var progress: Double = 0.5
4 var body: some View {
5 VStack {
6 CircularProgressBarView(progress: progress)
7 .frame(width: 150, height: 150)
9 Slider(value: $progress, in: 0...1)
10 .padding()
11 }
12 .padding()
13 }

In the above code snippet, we create a new SwiftUI view named ContentView. Inside this view, we declare a @State property called progress to track the progress value. We initialize it with a value of 0.5, representing 50% completion.

The CircularProgressBarView is then added to the view hierarchy with the progress value bound to the progress property. We also set a fixed frame size for the progress bar using the .frame modifier.

Customization and Enhancements

The circular progress bar presented in this tutorial is a starting point that you can customize and enhance further to fit your specific app design. Here are a few ideas to explore:

  • Adjust the stroke width and colors to match your app's visual style.
  • Add gradient colors to create a more visually appealing progress bar.
  • Customize the font, color, and positioning of the progress label.