Custom progress bar

Hi all,

I’m trying to recreate a progress bar for an onboarding process. I’m using BarProgressStyle with a customization feature but I can’t figure out to show it as I want and make it do what I want.

In the attachment, there is a screen showing the progress bar that I’m trying to recreate. This progress bar shows the progress when you go forward to the next screen but you can also go backwards by tapping on the rounded rectangles that are part of the progress bar.

Is there someone who can give some best practices or insights on how to do this?

Best,
Sem

Hi Sem,

I’m up for these kinds of challenges.

This is what I have come up with as a test project and these are the Views, Model and App components. Hope you can make sense of it.

App:

import SwiftUI

@main
struct OnBoardProgressApp: App {
    @State var progressManager = ProgressModel()
    
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environment(progressManager)
        }
    }
}

ContentView:

import SwiftUI

struct ContentView: View {
    @Environment(ProgressModel.self) var progressModel
    var body: some View {
        VStack {
            ProgressBar()
            Spacer()
            HStack {
                Button(action: {
                    if progressModel.progressAmount > 1 {
                        progressModel.progressAmount -= 1
                    }
                }, label: {
                    Text("Previous")
                })
                Button(action: {
                    if progressModel.progressAmount < 6 {
                        progressModel.progressAmount += 1
                    }
                }, label: {
                    Text("Next")
                })
            }
        }
        .padding()
    }
}

#Preview {
    ContentView()
        .environment(ProgressModel())
        .preferredColorScheme(.dark)
}

ProgressBar:

import SwiftUI

struct ProgressBar: View {
    @Environment(ProgressModel.self) var progressModel

    var body: some View {
        HStack {
            ForEach(1...6, id: \.self) { index in
                if index <= progressModel.progressAmount {
                    Button(action: {
                        progressModel.progressAmount = index
                    }, label: {
                        ProgressSegment(color: .white)
                    })
                    .buttonStyle(.plain)

                } else {
                    Button(action: {
                        progressModel.progressAmount = index
                    }, label: {
                        ProgressSegment(color: .gray)
                    })
                    .buttonStyle(.plain)

                }
            }
        }
    }
}

#Preview {
    ProgressBar()
        .preferredColorScheme(.dark)
        .environment(ProgressModel())
}

ProgressSegment:

import SwiftUI

struct ProgressSegment: View {
    let color: Color

    var body: some View {
        Rectangle()
            .fill(color)
            .frame(height: 4)
            .cornerRadius(2)
    }
}

#Preview {
    ProgressSegment(color: .white)
        .preferredColorScheme(.dark)
}

ProgressModel:

import Foundation
import Observation

@Observable class ProgressModel {
    var progressAmount = 1
}
2 Likes

Perfect solution. Thanks a lot!

That’s great. Glad to be of assistance.