An indeterminate linear ProgressView that works on iOS
It's strange. This SwiftUI code, for a linear-style ProgressView
, shows an indeterminate animation on macOS (Monterey 12.1) but not on iOS (15.2):
ProgressView()
.progressViewStyle(LinearProgressViewStyle())
So let's make our own
struct IndeterminateProgressView: View {
@State private var width: CGFloat = 0
@State private var offset: CGFloat = 0
@Environment(\.isEnabled) private var isEnabled
var body: some View {
Rectangle()
.foregroundColor(.gray.opacity(0.15))
.readWidth()
.overlay(
Rectangle()
.foregroundColor(Color.accentColor)
.frame(width: self.width * 0.26, height: 6)
.clipShape(Capsule())
.offset(x: -self.width * 0.6, y: 0)
.offset(x: self.width * 1.2 * self.offset, y: 0)
.animation(.default.repeatForever().speed(0.265), value: self.offset)
.onAppear{
withAnimation {
self.offset = 1
}
}
)
.clipShape(Capsule())
.opacity(self.isEnabled ? 1 : 0)
.animation(.default, value: self.isEnabled)
.frame(height: 6)
.onPreferenceChange(WidthPreferenceKey.self) { width in
self.width = width
}
}
}
You'll also need this read-width view modifier too, which reads the width of the view it's attached to. (I'd expect that most SwiftUI projects will already have this though!)
struct WidthPreferenceKey: PreferenceKey {
static var defaultValue: CGFloat = 0
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
value = max(value, nextValue())
}
}
private struct ReadWidthModifier: ViewModifier {
private var sizeView: some View {
GeometryReader { geometry in
Color.clear.preference(key: WidthPreferenceKey.self, value: geometry.size.width)
}
}
func body(content: Content) -> some View {
content.background(sizeView)
}
}
extension View {
func readWidth() -> some View {
self
.modifier(ReadWidthModifier())
}
}
What does it look like?

macOS LinearProgressViewStyle
(top) and the new IndeterminateLinearProgressView
(bottom). That's close enough for me.
First published 6 January 2022