03
Mar
2021
Material Indefinite Loading Bar in SwiftUI
Reading time: 1 min
This recipe shows how to implement an indefinite loading bar in SwiftUI. This kind of view is common in Android Material Design as it's sleek and takes up less space than a conventional, circular LoadingView
.
The end result looks like this:
The code for this is quite simple:
- Render a
Rectangle
that fits its parent's width. - Partly cover it by another
Rectangle
in itsoverlay
. Use aGeometryReader
to determine the overlay width. - Animate the overlay's translation by changing its offset:
minOffset
puts the top rectangle fully to the left of the bottom rectangle, effectively hiding it from sight.maxOffset
puts the top rectangle fully to the right, again hiding it from sight.
- Make sure that the
Animation
uses autoreverses: true inrepeatForever
in order to reset the top rectangle tominOffset
at each iteration.
Here's the code:
// the height of the bar
private let height: CGFloat = 4
// how much does the blue part cover the gray part (40%)
private let coverPercentage: CGFloat = 0.4
private let minOffset: CGFloat = -2
private let maxOffset = 1 / coverPercentage * abs(minOffset)
struct InfiniteProgressBar: View {
@State private var offset = minOffset;
var body: some View {
Rectangle()
.foregroundColor(.gray) // change the color as you see fit
.frame(height: height)
.overlay(GeometryReader { geo in
overlayRect(in: geo.frame(in: .local))
})
}
private func overlayRect(in rect: CGRect) -> some View {
let width = rect.width * coverPercentage
return Rectangle()
.foregroundColor(.blue)
.frame(width: width)
.offset(x: width * offset)
.onAppear {
withAnimation(Animation.linear(duration: 2.5).repeatForever(autoreverses: false)) {
self.offset = maxOffset;
}
}
}
}