Swift Timer()麻烦
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Swift Timer()麻烦相关的知识,希望对你有一定的参考价值。
我在Swift中做了一个简单的计时器。当秒数达到59秒时,一切都很好。而不是回到零,他们只是继续前进。有人能够指出我出错的地方以及为什么会这样吗?
@IBAction func startButtonDidTouch(_ sender: Any) {
if !timerIsRunning{
timer = Timer.scheduledTimer(timeInterval: 0.01, target: self, selector: #selector(self.updateTimer), userInfo: nil, repeats: true)
timerIsRunning = true
}
}
@objc func updateTimer() {
totalSeconds += 0.01
let totalSecondsTimes100: Int = Int(totalSeconds*100)
let minutes = Int(totalSeconds/60)
let timerChoice = Double(minutes)
let minStr = (minutes == 0) ? "00" : "0(minutes)"
let secStr = (totalSeconds < 9) ? "0(Float(totalSecondsTimes100)/100)" : "(Float(totalSecondsTimes100)/100)"
switch Int(timerChoice) {
case Int(timerCountdownLabel.text!)!:
timerLabel.text = "(minStr):(secStr)"
audioPlayer.play()
timer.invalidate()
timerIsRunning = false
default:
timerLabel.text = "(minStr):(secStr)"
}
}
答案
您应该将秒计算为:
let seconds = totalSeconds % 60
然后在计算seconds
而不是使用secStr
时使用totalSeconds
。
有更好的方法来编写代码:
@objc func updateTimer() {
totalSeconds += 0.01
let minutes = Int(totalSeconds) / 60
let seconds = totalSeconds.remainder(dividingBy: 60)
let timeStr = String(format: "%02d:%06.3f", minutes, seconds)
timerLabel.text = timeStr
if Int(timerCountdonwLabel.text!)! == minutes {
audioPlayer.play()
timer.invalidate()
timerIsRunning = false
}
}
你真的不应该通过添加0.01
到totalSeconds
来追踪时间。计时器不准确。你的时钟会随着时间而漂移。当你启动计时器并获得Date()
中的当前时间戳(Date()
)并获得两者之间的差异时,最好保存时间戳(updateTimer
)。
另一答案
这是一个定时器函数,输出格式分钟:秒:毫秒,与你的代码比较,你会发现你的代码有什么问题。
private weak var timer: Timer?
private var startTime: Double = 0
private var elapsed: Double = 0
private var time: Double = 0
private func startTimer(){
startTime = Date().timeIntervalSinceReferenceDate - elapsed
timer = Timer.scheduledTimer(timeInterval: (0.01), target: self, selector: #selector(updateTimeLabel), userInfo: nil, repeats: true)
}
private func stopTimer(){
elapsed = Date().timeIntervalSinceReferenceDate - startTime
timer?.invalidate()
}
@objc func updateTimeLabel(){
time = Date().timeIntervalSinceReferenceDate - startTime
let minutes = UInt8(time / 60.0)
let timeNoMin = time - (TimeInterval(minutes) * 60)
let seconds = UInt8(timeNoMin)
let timeNoSec = timeNoMin - (TimeInterval(seconds))
let milliseconds = UInt16(timeNoSec * 100)
let strMinutes = String(minutes)
var strSeconds = ""
if strMinutes == "0" {
strSeconds = String(seconds)
}
else {
strSeconds = String(format: "%02d", seconds)
}
let strMilliseconds = String(format: "%02d"), milliseconds)
if strMinutes != "0" {
timerLabel.text = "(strMinutes):(strSeconds).(strMilliseconds)"
}
else {
timerLabel.text = "(strSeconds).(strMilliseconds)"
}
}
另一答案
要从浮点总秒数中获得分钟和秒数,elapsed
您可以:
- 要获得分钟,除以60.0并截断为最接近的整数:
let minutes = Int(elapsed / 60)
- 要获得秒数,请通过以下方式获取余数:
let seconds = elapsed - Double(minutes) * 60
要么let seconds = elapsed.truncatingRemainder(dividingBy: 60)
其他几点意见:
- 当屏幕刷新率通常限制在每秒60帧时,每0.01秒运行一次计时器是没有意义的。如果要以最大频率更新它,请使用
CADisplayLink
,它不仅可以获得最大屏幕刷新率,还可以最佳激发,以便在下一帧渲染之前进行更新。 - 您不应该使用计时器将经过的时间增加0.01(或任何固定的间隔),因为您无法保证它将实际以该频率触发。例如,如果某个东西暂时阻塞主线程200毫秒,则不希望这会影响您对已经过的时间量的计算。 相反,保存计时器启动时的开始时间,每次计时器触发时重新计算已用时间并相应地格式化结果。
- 为了进一步复杂化,你甚至不应该比较
Date()
实例(或CFAbsoluteTimeGetCurrent()
值)因为,如the documentation warns us: 重复调用此函数并不能保证单调增加结果。由于与外部时间参考同步或由于时钟的明确用户改变,系统时间可能减少。 相反,您应该使用基于mach_absolute_time
的计算(例如由CACurrentMediaTime()
返回),为此确保重复调用返回准确的经过时间计算。 如果您的应用程序将开始时间保存在持久存储中,则应该使用Date()
或CFAbsoluteTimeGetCurrent()
,以便稍后在应用程序重新启动时(可能在设备重新启动后)检索,以呈现应用程序启动之间经过的时间的效果。但这是一个相当狭窄的边缘情况。
无论如何,这产生:
var start: CFTimeInterval?
weak var displayLink: CADisplayLink?
func startTimer() {
self.displayLink?.invalidate() // just in case timer had already been started
start = CACurrentMediaTime()
let displayLink = CADisplayLink(target: self, selector: #selector(handleDisplayLink(_:)))
displayLink.preferredFramesPerSecond = 100 // in case you're using a device that can render more than 60 fps
displayLink.add(to: .main, forMode: .commonModes)
self.displayLink = displayLink
}
@objc func handleDisplayLink(_ displayLink: CADisplayLink) {
let elapsed = CACurrentMediaTime() - start!
let minutes = Int(elapsed / 60)
let seconds = elapsed - Double(minutes) * 60
let string = String(format: "%02d:%05.2f", minutes, seconds)
label.text = string
}
func stopTimer() {
displayLink?.invalidate()
}
以上是关于Swift Timer()麻烦的主要内容,如果未能解决你的问题,请参考以下文章
使用 Swift Combine 创建一个 Timer Publisher
swift开发之 -- 自动轮播图(UIScrollView+UIPageControl+Timer)