Skip to content

Instantly share code, notes, and snippets.

@aheze
Last active May 14, 2023 20:08
Show Gist options
  • Save aheze/4767ba08dcc3f9ad846706ed5a9f31c8 to your computer and use it in GitHub Desktop.
Save aheze/4767ba08dcc3f9ad846706ed5a9f31c8 to your computer and use it in GitHub Desktop.
struct ContentView: View {
var numberOfSegments = 4
@State var currentSegment: Int?
@State var autoIncrementing = false
var period = CGFloat(0.8)
var body: some View {
VStack {
Text("Progress")
.bold()
stripes
VStack {
if let currentSegment = currentSegment {
Text("\(currentSegment)")
.font(.system(size: 64).bold())
}
}
.frame(height: 300)
.frame(maxWidth: .infinity)
.background(Color.white.opacity(0.25))
.cornerRadius(16)
.padding()
.animation(nil)
Button(autoIncrementing ? "Stop Auto Incrementing" : "Start Auto Incrementing") {
autoIncrementing.toggle()
if autoIncrementing {
start()
}
}
}
.foregroundColor(.white)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.padding()
.background(Color.blue)
}
var stripes: some View {
HStack(spacing: 4) {
ForEach(0 ..< numberOfSegments) { index in
/// progress bar track
Capsule()
.fill(.white)
.opacity(0.25)
/// the overlay that animates
.overlay {
if let currentSegment = currentSegment {
GeometryReader { geometry in
Capsule()
.fill(.white)
.frame(width: currentSegment >= index ? geometry.size.width : 0)
}
}
}
}
}
.frame(height: 6)
}
/// recursively increment the progress
func start() {
guard autoIncrementing else { return }
increment {
start()
}
}
func increment(completion: @escaping (() -> Void)) {
/// after fading out, fade it back in
if currentSegment == nil {
currentSegment = -1
}
/// animate the progress width
withAnimation(.linear(duration: period)) {
if let currentSegment = currentSegment {
/// If the current segment isn't the last one...
if currentSegment < numberOfSegments - 1 {
/// ... animate the segment
let currentSegment = currentSegment + 1
self.currentSegment = currentSegment
} else {
/// otherwise, slowly fade out everything
self.currentSegment = nil
}
} else {
currentSegment = 0 /// animate to the first segment if it was previously nil
}
}
/// call completion when done
DispatchQueue.main.asyncAfter(deadline: .now() + period) {
completion()
}
}
}
@aheze
Copy link
Author

aheze commented Nov 9, 2022

Screen.Recording.2022-11-08.at.7.47.27.PM.mov

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment