SwiftUI - 如何制作 Start/Stop 计时器
SwiftUI - How to Make A Start/Stop Timer
我的目标是在 SwiftUI 中创建一个以 0 开头的视图。当您按下视图时,计时器应该开始向上计数,再次点击会停止计时器。最后,当您再次点击以启动计时器时,计时器应从 0 开始。
这是我当前的代码:
import SwiftUI
struct TimerView: View {
@State var isTimerRunning = false
@State private var endTime = Date()
@State private var startTime = Date()
let timer = Timer.publish(every: 0.001, on: .main, in: .common).autoconnect()
var tap: some Gesture {
TapGesture(count: 1)
.onEnded({
isTimerRunning.toggle()
})
}
var body: some View {
Text("\(endTime.timeIntervalSince1970 - startTime.timeIntervalSince1970)")
.font(.largeTitle)
.gesture(tap)
.onReceive(timer) { input in
startTime = isTimerRunning ? startTime : Date()
endTime = isTimerRunning ? input : endTime
}
}
}
此代码会导致计时器立即启动并且永不停止,即使我点击它也是如此。计时器也会倒退(变成负数)而不是前进。
有人可以帮我理解我做错了什么吗?另外,我想知道这是否是一个很好的计时器整体策略(使用 Timer.publish)。
谢谢!
这里是固定版本。看看我所做的更改。
如果计时器为 运行,.onReceive
现在会更新 timerString
。 timeString 是现在(即 Date()
)和 startTime
. 之间的间隔
- 点击计时器设置
startTime
如果不是 运行。
struct TimerView: View {
@State var isTimerRunning = false
@State private var startTime = Date()
@State private var timerString = "0.00"
let timer = Timer.publish(every: 0.01, on: .main, in: .common).autoconnect()
var body: some View {
Text(self.timerString)
.font(Font.system(.largeTitle, design: .monospaced))
.onReceive(timer) { _ in
if self.isTimerRunning {
timerString = String(format: "%.2f", (Date().timeIntervalSince( self.startTime)))
}
}
.onTapGesture {
if !isTimerRunning {
timerString = "0.00"
startTime = Date()
}
isTimerRunning.toggle()
}
}
}
上面的版本虽然简单,但让我感到困扰的是 Timer
一直在发布。我们只需要在计时器为 运行.
时发布 Timer
这是启动和停止 Timer
:
的版本
struct TimerView: View {
@State var isTimerRunning = false
@State private var startTime = Date()
@State private var timerString = "0.00"
@State private var timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
var body: some View {
Text(self.timerString)
.font(Font.system(.largeTitle, design: .monospaced))
.onReceive(timer) { _ in
if self.isTimerRunning {
timerString = String(format: "%.2f", (Date().timeIntervalSince( self.startTime)))
}
}
.onTapGesture {
if isTimerRunning {
// stop UI updates
self.stopTimer()
} else {
timerString = "0.00"
startTime = Date()
// start UI updates
self.startTimer()
}
isTimerRunning.toggle()
}
.onAppear() {
// no need for UI updates at startup
self.stopTimer()
}
}
func stopTimer() {
self.timer.upstream.connect().cancel()
}
func startTimer() {
self.timer = Timer.publish(every: 0.01, on: .main, in: .common).autoconnect()
}
}
我的目标是在 SwiftUI 中创建一个以 0 开头的视图。当您按下视图时,计时器应该开始向上计数,再次点击会停止计时器。最后,当您再次点击以启动计时器时,计时器应从 0 开始。
这是我当前的代码:
import SwiftUI
struct TimerView: View {
@State var isTimerRunning = false
@State private var endTime = Date()
@State private var startTime = Date()
let timer = Timer.publish(every: 0.001, on: .main, in: .common).autoconnect()
var tap: some Gesture {
TapGesture(count: 1)
.onEnded({
isTimerRunning.toggle()
})
}
var body: some View {
Text("\(endTime.timeIntervalSince1970 - startTime.timeIntervalSince1970)")
.font(.largeTitle)
.gesture(tap)
.onReceive(timer) { input in
startTime = isTimerRunning ? startTime : Date()
endTime = isTimerRunning ? input : endTime
}
}
}
此代码会导致计时器立即启动并且永不停止,即使我点击它也是如此。计时器也会倒退(变成负数)而不是前进。
有人可以帮我理解我做错了什么吗?另外,我想知道这是否是一个很好的计时器整体策略(使用 Timer.publish)。
谢谢!
这里是固定版本。看看我所做的更改。
-
如果计时器为 运行,
.onReceive
现在会更新timerString
。 timeString 是现在(即Date()
)和startTime
. 之间的间隔
- 点击计时器设置
startTime
如果不是 运行。
struct TimerView: View {
@State var isTimerRunning = false
@State private var startTime = Date()
@State private var timerString = "0.00"
let timer = Timer.publish(every: 0.01, on: .main, in: .common).autoconnect()
var body: some View {
Text(self.timerString)
.font(Font.system(.largeTitle, design: .monospaced))
.onReceive(timer) { _ in
if self.isTimerRunning {
timerString = String(format: "%.2f", (Date().timeIntervalSince( self.startTime)))
}
}
.onTapGesture {
if !isTimerRunning {
timerString = "0.00"
startTime = Date()
}
isTimerRunning.toggle()
}
}
}
上面的版本虽然简单,但让我感到困扰的是 Timer
一直在发布。我们只需要在计时器为 运行.
Timer
这是启动和停止 Timer
:
struct TimerView: View {
@State var isTimerRunning = false
@State private var startTime = Date()
@State private var timerString = "0.00"
@State private var timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
var body: some View {
Text(self.timerString)
.font(Font.system(.largeTitle, design: .monospaced))
.onReceive(timer) { _ in
if self.isTimerRunning {
timerString = String(format: "%.2f", (Date().timeIntervalSince( self.startTime)))
}
}
.onTapGesture {
if isTimerRunning {
// stop UI updates
self.stopTimer()
} else {
timerString = "0.00"
startTime = Date()
// start UI updates
self.startTimer()
}
isTimerRunning.toggle()
}
.onAppear() {
// no need for UI updates at startup
self.stopTimer()
}
}
func stopTimer() {
self.timer.upstream.connect().cancel()
}
func startTimer() {
self.timer = Timer.publish(every: 0.01, on: .main, in: .common).autoconnect()
}
}