Skip to content

Instantly share code, notes, and snippets.

@Koshimizu-Takehito
Last active March 5, 2025 10:01
Show Gist options
  • Save Koshimizu-Takehito/f30712ddea1230a58062594b86ad6ef3 to your computer and use it in GitHub Desktop.
Save Koshimizu-Takehito/f30712ddea1230a58062594b86ad6ef3 to your computer and use it in GitHub Desktop.
カウントダウンアニメーション
import SwiftUI
// https://x.com/TAAT626/status/1895841081365053901
// https://gist.github.com/TAATHub/8f9e7d987c82ef0eea62d2e420d51144
struct CountdownView: View {
@State private var counter = Counter()
var body: some View {
let radius = 120.0
ZStack {
Text("\(Int(counter.count + 0.99))")
.fontDesign(.rounded)
.font(.system(size: 60, weight: .bold))
.monospacedDigit()
.contentTransition(.numericText(countsDown: true))
.transaction { $0.animation = counter.count > 1 ? $0.animation : nil }
.animation(.default, value: counter.count)
ZStack {
ForEach(Array(0..<36), id: \.self) { angle in
Capsule()
.frame(width: 8, height: 24)
.offset(x: 0, y: radius - 24.0/2.0)
.rotationEffect(.degrees(Double(angle) * 10))
}
}
.mask {
Circle()
.trim(from: 0, to: degree)
.stroke(lineWidth: 2 * 24)
.frame(width: radius * 2, height: radius * 2)
.rotationEffect(.degrees(-90.0 - 5.0))
}
}
.frame(width: 2 * radius, height: 2 * radius)
.clipShape(.circle)
.contentShape(.circle)
.foregroundStyle(counter.count > 0 ? .black : .red)
.onTapGesture {
Task { await counter.restart() }
}
}
private var degree: Double {
1.0 - counter.count.truncatingRemainder(dividingBy: 1.0)
}
}
#Preview {
CountdownView()
}
import Foundation
@MainActor
@Observable
final class Counter {
private(set) var count: Double = 10
private(set) var task: Task<Void, Never>? {
didSet { oldValue?.cancel() }
}
func restart() async {
count = 10
task = Task { [weak self] in
let interval = 1.0 / 360.0
let timer = Timer
.publish(every: interval, on: .main, in: .common)
.autoconnect()
for await _ in timer.values {
guard let self, count > 0 else {
break
}
count -= interval
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment