这是我当前的工作解决方案,但看起来很奇怪。我有点研究这些数字,直到它起作用,但我不完全理解数学。下面是我的代码。第二个圆圈
.trim
是我认为主要问题所在。
struct ContentView: View {
@State private var timeRemaining: TimeInterval = 10
@State private var startTime: Double = 10
@State private var timer: Timer?
@State private var isRunning: Bool = false
var body: some View {
ZStack {
Circle()
.trim(from: 0.1, to: 0.9)
.stroke(style: StrokeStyle(lineWidth: 20, lineCap: .round, lineJoin: .round))
.rotationEffect(.degrees(90))
.opacity(0.3)
Circle()
.trim(from: 0.1, to: CGFloat((1.1 - (timeRemaining / (startTime) - 0.026)) / 1.25))
.stroke(style: StrokeStyle(lineWidth: 20, lineCap: .round, lineJoin: .round))
.rotationEffect(.degrees(90))
.animation(.easeInOut(duration: 1))
Text(formattedTime())
.font(.largeTitle)
.bold()
}
.frame(width: 250)
Button {
isRunning.toggle()
if isRunning {
startTimer()
} else {
stopTimer()
}
} label: {
Image(systemName: isRunning ? "stop.fill" : "play.fill")
.foregroundStyle(.foreground)
.frame(width: 50, height: 50)
.font(.largeTitle)
.padding()
}
}
private func startTimer() {
timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in
if timeRemaining > 0 {
timeRemaining -= 1
} else {
stopTimer()
}
}
}
private func stopTimer() {
isRunning = false
timer?.invalidate()
timeRemaining = 10
startTime = Double(timeRemaining)
}
private func formattedTime() -> String {
let minutes = Int(timeRemaining) / 60
let second = Int(timeRemaining) % 60
return String(format: "%02d:%02d", minutes, second)
}
}
您可以让动画在计时器倒计时的同时运行。它也需要
linear
。
为了使点在开始时可见,初始位置必须大于 0.1。 0.101 就可以了。
Circle()
.trim(from: 0.1, to: isRunning ? 0.9 : 0.101)
.stroke(style: StrokeStyle(lineWidth: 20, lineCap: .round, lineJoin: .round))
.rotationEffect(.degrees(90))
.animation(.linear(duration: isRunning ? 10 : 0), value: isRunning)