I'm trying to implement a countdown view in SwiftUI as follows:
struct CountdownView: View {
private let duration: Double
private let period: Double
@State
var timeRemaining: Double = 20.0
init(duration: Double, period: Double) {
self.duration = duration
self.period = period
_timeRemaining = State(initialValue: duration)
}
private let timer = Timer.publish(every: 0.1, on: .main, in: .common).autoconnect()
private var endAngle: Double { 360.0 * (1 - timeRemaining / period) - 90.0 }
var body: some View {
Arc(startAngle: .degrees(270), endAngle: .degrees(endAngle), clockwise: true)
.foregroundColor(Color.blue)
.animation(.linear(duration: 0.1))
.frame(width: 50, height: 50)
.onReceive(timer) { _ in
if self.timeRemaining > 0 {
withAnimation {
self.timeRemaining -= 0.1
}
}
}
}
}
struct Arc: Shape {
let startAngle: Angle
let endAngle: Angle
let clockwise: Bool
func path(in rect: CGRect) -> Path {
var path = Path()
let radius = max(rect.size.width, rect.size.height) / 2
let centerPoint = CGPoint(x: rect.midX, y: rect.midY)
path.move(to: centerPoint)
path.addArc(center: centerPoint,
radius: radius,
startAngle: startAngle,
endAngle: endAngle,
clockwise: clockwise
)
return path
}
}
What I expect is something like this:
However, the View doesn't animate at all, it's just stuck in the initial position indefinitely. But if I initialize the state with a literal at once like this @State var timeRemaining: Double = 20.0
and replace period
with some literal number, then the animation works.
What might be the cause of this?