Creating Custom Progress Bars in SwiftUI
Progress bars are essential UI elements that provide visual feedback about ongoing processes. While SwiftUI provides a basic ProgressView, creating custom progress bars can enhance your app's visual appeal and user experience. In this post, we'll explore different ways to implement progress bars in SwiftUI.
1. Basic Linear Progress Bar
Let's start with a simple custom linear progress bar:
struct CustomProgressBar: View {
let progress: Double
var body: some View {
GeometryReader { geometry in
ZStack(alignment: .leading) {
Rectangle()
.foregroundColor(.gray.opacity(0.3))
Rectangle()
.foregroundColor(.blue)
.frame(width: min(CGFloat(self.progress) * geometry.size.width,
geometry.size.width))
}
}
.cornerRadius(8)
.frame(height: 8)
}
}
// Usage Example
struct ContentView: View {
@State private var progress = 0.0
var body: some View {
VStack {
CustomProgressBar(progress: progress)
.padding()
Button("Increment") {
withAnimation {
progress = min(1.0, progress + 0.2)
}
}
}
}
}
2. Animated Circular Progress Bar
For a more dynamic look, here's a circular progress indicator:
struct CircularProgressBar: View {
let progress: Double
let lineWidth: CGFloat = 10
var body: some View {
ZStack {
Circle()
.stroke(
Color.gray.opacity(0.3),
lineWidth: lineWidth
)
Circle()
.trim(from: 0, to: CGFloat(progress))
.stroke(
Color.blue,
style: StrokeStyle(
lineWidth: lineWidth,
lineCap: .round
)
)
.rotationEffect(.degrees(-90))
// Optional: Add animation
.animation(.easeInOut, value: progress)
}
}
}
// Usage with percentage label
struct CircularProgressView: View {
@State private var progress = 0.0
var body: some View {
ZStack {
CircularProgressBar(progress: progress)
.frame(width: 100, height: 100)
Text("\(Int(progress * 100))%")
.font(.system(.title3, design: .rounded))
.bold()
}
.onAppear {
withAnimation(.easeInOut(duration: 2)) {
progress = 0.75
}
}
}
}
3. Progress Bar with Gradient
Add some style with gradients:
struct GradientProgressBar: View {
let progress: Double
let gradient = LinearGradient(
colors: [.blue, .purple],
startPoint: .leading,
endPoint: .trailing
)
var body: some View {
GeometryReader { geometry in
ZStack(alignment: .leading) {
Rectangle()
.foregroundColor(.gray.opacity(0.3))
Rectangle()
.fill(gradient)
.frame(width: min(CGFloat(self.progress) * geometry.size.width,
geometry.size.width))
}
}
.cornerRadius(8)
.frame(height: 8)
}
}
4. Indeterminate Progress Bar
For scenarios where progress can't be determined:
struct IndeterminateProgressBar: View {
@State private var isAnimating = false
var body: some View {
GeometryReader { geometry in
ZStack(alignment: .leading) {
Rectangle()
.foregroundColor(.gray.opacity(0.3))
Rectangle()
.foregroundColor(.blue)
.frame(width: geometry.size.width * 0.3)
.offset(x: isAnimating ? geometry.size.width : -geometry.size.width * 0.3)
}
}
.cornerRadius(8)
.frame(height: 8)
.onAppear {
withAnimation(
.linear(duration: 1)
.repeatForever(autoreverses: false)
) {
isAnimating = true
}
}
}
}
Best Practices
- Always animate progress changes for smooth transitions
- Use appropriate colors that match your app's theme
- Consider accessibility - provide alternative text for screen readers
- Maintain consistent sizing across your app
Accessibility Enhancement
Make your progress bars accessible:
struct AccessibleProgressBar: View {
let progress: Double
let label: String
var body: some View {
CustomProgressBar(progress: progress)
.accessibilityValue("\(Int(progress * 100)) percent complete")
.accessibilityLabel(label)
.accessibilityProgressLabels("0 percent", "100 percent")
}
}
These progress bar implementations provide a solid foundation for your SwiftUI apps. Remember to consider your specific use case when choosing between different styles and animations. The key is to provide clear visual feedback while maintaining good performance.
Conclusion
Custom progress bars in SwiftUI offer great flexibility in design while maintaining smooth animations and good performance. Whether you need a simple linear progress bar or a more complex animated indicator, SwiftUI provides the tools to create exactly what you need.