Last Run on XCODE 14.2 / iOS 16.2 / Swift 5.7.2

Progress View Ring Link to heading

I was building a Timer app and needed a visual indicator to display the the amount of time left while the countdown was running. A circular progress bar similar to the fitness/activity rings of Apple Watch made the most sense. Sadly, Apple currently doesn’t provide a good API to use such a view in your own apps. However, building such a View in SwiftUI is pretty simple.

1. Draw a circle Link to heading

To build something simlar in SwiftUI, first we need is a builtin View called Circle(), that can be further customized using .stroke.

Circle()
    .stroke(.green,style: StrokeStyle(lineWidth: 8))

2. Trim the circle Link to heading

Next we want is for the Circle to be drawn as the the progress moves forward with a full circle signifying the completion of the task being visualized. To achieve this effect, we can use a modifier called .trim that takes two CGFloat(s) a startFraction and an endFraction for the amount of the curve to draw. A thing to keep in mind is the initial start point for the circle drawn. It starts at middle of the right side in the View, and since I wanted the progress circle to be drawn from the top I use a .rotationEffect modifier with an angle of -90 degrees to adjust for it.

Circle()
    .trim(from: 0, to: 0.3)
    .stroke(.green,style: StrokeStyle(lineWidth: 8))
    .rotationEffect(Angle(degrees: -90))

3. Shadow Circle Link to heading

The visual effect I was aiming for required a progress circle on top of a lighter circle. This meant having two overlapping circles enclosed. This can be achieved with the code snippet below using a ZStack and providing a smaller opacity value to the bottom circle.

ZStack {
    Circle()
        .stroke(.green, style: StrokeStyle(lineWidth: 4))
        .opacity(0.3)

    Circle()
        .trim(from: 0, to: progress)
        .stroke(.green,style: StrokeStyle(lineWidth: 8))
        .rotationEffect(Angle(degrees: -90))
}

Code Listing Link to heading

This is the complete code listing to create a Progress View Ring in your SwiftUI project.

import SwiftUI

struct RingProgressView: View {
    var progress: Double
    var body: some View {
        VStack {
            Text("Progress View")
                .font(.largeTitle)
            ZStack {
                Circle()
                    .stroke(.green, style: StrokeStyle(lineWidth: 4))
                    .opacity(0.3)

                Circle()
                    .trim(from: 0, to: progress)
                    .stroke(.green,style: StrokeStyle(lineWidth: 8))
                    .rotationEffect(Angle(degrees: -90))
            }
            .padding()
        }
    }
}

struct RingProgressView_Previews: PreviewProvider {
    static var previews: some View {
        RingProgressView(progress: 0.3)
    }
}