我有一个使用Timer
进行倒计时的应用。倒计时会跟踪多个步骤(所有时间间隔相同)以及剩余的总时间,并相应地更新2个单独的UILabel。有时,标签将不同步。
我不能肯定地说,但我认为这可能仅在我暂停倒计时时才会发生,通常是在第一步之后。在最后一步中,最明显的是两个标签应该显示相同的确切内容,但有时会相差1秒。
另一个棘手的事情是,有时在时间不同步后暂停并重新开始会使其恢复同步。
我的猜测是我在暂停代码和/或步骤之间的移动中发生了一些奇怪的事情,或者可能是TimeIntervals的计算和格式化。另外,我在计算出的TimeIntervals上使用rounded()
,因为我注意到仅每1秒更新一次计时器,标签将冻结并跳过很多秒。但是我不确定这是否是解决此问题的最佳方法。
这里是相关代码。 (仍然需要进行重构,但希望它很容易理解,我仍然是初学者)
@IBAction func playPauseTapped(_ sender: Any) {
if timerState == .running {
//pause timer
pauseAnimation()
timer.invalidate()
timerState = .paused
pausedTime = Date()
playPauseButton.setImage(UIImage(systemName: "play.circle"), for: .normal)
} else if timerState == .paused {
//resume paused timer
guard let pause = pausedTime else { return }
let pausedInterval = Date().timeIntervalSince(pause)
startTime = startTime?.addingTimeInterval(pausedInterval)
endTime = endTime?.addingTimeInterval(pausedInterval)
currentStepEndTime = currentStepEndTime?.addingTimeInterval(pausedInterval)
pausedTime = nil
startTimer()
resumeAnimation()
timerState = .running
playPauseButton.setImage(UIImage(systemName: "pause.circle"), for: .normal)
} else {
//first run of brand new timer
startTimer()
startProgressBar()
startTime = Date()
if let totalTime = totalTime {
endTime = startTime?.addingTimeInterval(totalTime)
}
currentStepEndTime = Date().addingTimeInterval(recipeInterval)
timerState = .running
playPauseButton.setImage(UIImage(systemName: "pause.circle"), for: .normal)
currentWater += recipeWater[recipeIndex]
currentWeightLabel.text = "\(currentWater)g"
}
}
func startTimer() {
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(runTimer), userInfo: nil, repeats: true)
}
@objc func runTimer() {
let currentTime = Date()
guard let totalTimeLeft = endTime?.timeIntervalSince(currentTime).rounded() else { return }
guard let currentInterval = currentStepEndTime?.timeIntervalSince(currentTime).rounded() else { return }
//end of current step
if currentInterval <= 0 {
//check if end of recipe
if recipeIndex < recipeWater.count - 1 {
//move to next step
totalTimeLabel.text = totalTimeLeft.stringFromTimeInterval()
currentStepEndTime = Date().addingTimeInterval(recipeInterval)
startProgressBar()
currentStepTimeLabel.text = recipeInterval.stringFromTimeInterval()
stepsTime += recipeInterval
recipeIndex += 1
//update some ui
} else {
//last step
currentStepTimeLabel.text = "00:00"
totalTimeLabel.text = "00:00"
timer.invalidate()
//alert controller saying finished
}
} else {
//update time labels
currentStepTimeLabel.text = currentInterval.stringFromTimeInterval()
totalTimeLabel.text = totalTimeLeft.stringFromTimeInterval()
}
}
extension TimeInterval {
func stringFromTimeInterval() -> String {
let time = NSInteger(self)
let seconds = time % 60
let minutes = (time / 60) % 60
return String(format: "%0.2d:%0.2d",minutes,seconds)
}
}
编辑更新:我尝试了一些不同的操作,但仍然遇到相同的问题。我开始测试打印TimeInterval和格式化的字符串,以进行比较并查看发生了什么。肯定是某种舍入错误。
Total - 173.50678288936615 / 02:54
Step - 39.00026595592499 / 00:39
Total - 172.5073879957199 / 02:53
Step - 38.00087106227875 / 00:38
Total - 171.1903439760208 / 02:51
Step - 36.68382704257965 / 00:37
Total - 170.19031596183777 / 02:50
Step - 35.683799028396606 / 00:36
如您所见,总时间从2:53跳到2:51,但是步进计时器保持一致。原因是TimeInterval总计从172.5(向上舍入)到171.19(向下舍入)。
我还看着计时器倒计时而没有碰到暂停,它可靠地保持同步。因此,我将其范围缩小到我的暂停代码。
已解决我的问题,并在此处张贴以供后代参考。我最终完成了totalTimeLeft
和currentInterval
全局属性。然后,在暂停和恢复时,我没有使用已暂停的时间并将其添加到endTime
,而是仅使用了从上次totalTimeLeft
触发并执行currentInterval
时仍然存储的Timer
和endTime = Date().addingTimeInterval(totalTimeLeft)
值与间隔时间相同。这消除了暂停时间所增加的奇怪数量,而这些数量会弄乱四舍五入。