Last active
May 14, 2023 20:08
-
-
Save aheze/4767ba08dcc3f9ad846706ed5a9f31c8 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Screen.Recording.2022-11-08.at.7.47.27.PM.mov