-
-
Save durul/9fec4b4b50e99b25816472a3bd4e0c0a 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
import SwiftUI | |
import Combine | |
struct CountdownView: View { | |
let countdownSeconds: Int = 10 | |
let numberOfDivision: Int = 36 | |
let handSize: CGSize = .init(width: 8, height: 24) | |
let radius: CGFloat = 100 | |
@State var count: Int = 10 | |
@State var degree: Double = 360 | |
@State var timer: AnyCancellable? | |
var degreeInterval: CGFloat { | |
360.0 / Double(numberOfDivision) | |
} | |
var body: some View { | |
ZStack { | |
Text("\(count)") | |
.fontDesign(.rounded) | |
.font(.system(size: 60, weight: .bold)) | |
.contentTransition(.numericText(countsDown: true)) // This let countdown text transition downwards | |
.animation(.default, value: count) | |
ZStack { | |
// Place hands in a circle | |
ForEach(Array(0..<numberOfDivision), id: \.self) { angle in | |
Capsule() | |
.frame(width: handSize.width, height: handSize.height) | |
.offset(x: 0, y: radius) | |
.rotationEffect(.degrees(Double(angle) * degreeInterval)) | |
} | |
} | |
.mask { | |
Circle() | |
.trim(from: 0, to: degree / 360) // By changing degree using withAnimation, the trim fraction also changes, creating hands animation | |
.stroke(lineWidth: handSize.height) | |
.frame(width: radius * 2, height: radius * 2) | |
.rotationEffect(.degrees(-90.0 - degreeInterval/2)) // Let hands animation start from upward (adjusted to show entire first hand) | |
} | |
} | |
.foregroundStyle(count > 0 ? .black : .red) | |
.onTapGesture { | |
count = countdownSeconds | |
resetTimer() | |
animateRing() | |
timer = Timer.publish(every: 1, on: .main, in: .common) | |
.autoconnect() | |
.sink { _ in | |
guard count > 0 else { | |
resetTimer() | |
return | |
} | |
count -= 1 | |
if count > 0 { | |
animateRing() | |
} | |
} | |
} | |
} | |
private func resetTimer() { | |
timer?.cancel() | |
timer = nil | |
} | |
private func animateRing() { | |
degree = 0 | |
withAnimation(.linear(duration: 1)) { | |
degree += 360 | |
} | |
} | |
} | |
#Preview { | |
CountdownView() | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment