使用 Swift 将实时秒表计时器格式化为百分之一
Format realtime stopwatch timer to the hundredth using Swift
我有一个应用程序使用 NSTimer 以厘秒(0.01 秒)更新间隔显示 运行 秒表,字符串格式为 00:00.00 (mm:ss.SS)。 (基本上克隆了iOS内置秒表以集成到实时运动计时数学问题中,将来可能需要毫秒精度)
我使用(误用?)NSTimer 来强制更新 UILabel。如果用户按下 Start,这是用于开始重复该功能的 NSTimer 代码:
displayOnlyTimer = NSTimer.scheduledTimerWithTimeInterval(0.01, target: self, selector: Selector("display"), userInfo: nil, repeats: true)
下面是上面的 NSTimer 执行的函数:
func display() {
let currentTime = CACurrentMediaTime() - timerStarted + elapsedTime
if currentTime < 60 {
timeDisplay.text = String(format: "%.2f", currentTime)
}else if currentTime < 3600 {
var minutes = String(format: "%00d", Int(currentTime/60))
var seconds = String(format: "%05.2f", currentTime % 60)
timeDisplay.text = minutes + ":" + seconds
}else {
var hours = String(format: "%00d", Int(currentTime/3600))
var minutes = String(format: "%02d", (Int(currentTime/60)-(Int(currentTime/3600)*60)))
var seconds = String(format: "%05.2f", currentTime % 60)
timeDisplay.text = hours + ":" + minutes + ":" + seconds
}
}
至少会同时显示2个links 运行。当所有其他元素都在起作用时,此方法是否效率太低?
当用户按下 stop/pause/reset 时,显示会在不使用 NSTimer 的情况下更新。我没有找到任何直接翻译成 Swift 的内容。我相当确定我正在使用一种低效的方法来强制快速更新 UIView 中的文本 UILabel。
更多详情:
我正在为 运行 计时器格式 (mm:ss.SS) 编写更简洁的代码。完成后我会再更新一次。
更新:感谢 Rob 和 jtbandes 回答了我的两个问题(格式化方法和显示更新方法)。
用 CADisplayLink():
替换 NSTimer(见上文)很容易
displayLink = CADisplayLink(target: self, selector: Selector("display"))
displayLink.addToRunLoop(NSRunLoop.currentRunLoop(), forMode: NSRunLoopCommonModes)
然后替换
代码中的所有实例
displayOnlyTimer.invalidate()
与
displayLink.paused = true
(这将暂停显示 link 更新)
要快速 UI 更新,您应该使用 CADisplayLink。任何比显示刷新率快的东西都是对处理能力的浪费,因为它在物理上无法显示。它还提供了前一帧的 timestamp
,因此您可以尝试预测下一帧的时间。
您正在多次计算 CACurrentMediaTime() - timerStarted + elapsedTime
。我建议只做一次并将其保存在局部变量中。
考虑使用 NSDateComponentsFormatter。尝试重用格式化程序的一个实例,而不是每次都创建一个新实例(这通常是最昂贵的部分)。总的来说,字符串操作越少越好。
您可以在显示方法的开始和结束处检查 CACurrentMediaTime,以了解需要多长时间。理想情况下,它应该 much 小于 16.6ms。在 Xcode 调试导航器中密切关注 CPU 使用情况(和一般功耗)。
我今天正在解决同样的问题并找到了这个答案。 Rob 和 jtbandes 的建议很有帮助,我能够 assemble 从 Internet 上找到干净有效的解决方案。谢谢你们。感谢 mothy 的提问。
我决定使用 CADisplayLink,因为没有必要比屏幕更新更频繁地触发计时器的回调:
class Stopwatch: NSObject {
private var displayLink: CADisplayLink!
//...
override init() {
super.init()
self.displayLink = CADisplayLink(target: self, selector: "tick:")
displayLink.paused = true
displayLink.addToRunLoop(NSRunLoop.mainRunLoop(), forMode: NSRunLoopCommonModes)
//...
}
//...
}
我通过每次滴答将 elapsedTime 变量递增 displayLink.duration 来跟踪时间:
var elapsedTime: CFTimeInterval!
override init() {
//...
self.elapsedTime = 0.0
//...
}
func tick(sender: CADisplayLink) {
elapsedTime = elapsedTime + displayLink.duration
//...
}
时间格式化通过 NSDateFormatter 完成:
private let formatter = NSDateFormatter()
override init() {
//...
formatter.dateFormat = "mm:ss,SS"
}
func elapsedTimeAsString() -> String {
return formatter.stringFromDate(NSDate(timeIntervalSinceReferenceDate: elapsedTime))
}
UI 可以在秒表每次报价时调用的回调闭包中更新:
var callback: (() -> Void)?
func tick(sender: CADisplayLink) {
elapsedTime = elapsedTime + displayLink.duration
// Calling the callback function if available
callback?()
}
这就是您在 ViewController 中使用秒表所需要做的全部工作:
let stopwatch = Stopwatch()
stopwatch.callback = self.tick
func tick() {
elapsedTimeLabel.text = stopwatch.elapsedTimeAsString()
}
这里是 Stopwatch 的完整代码和使用指南的要点:
https://gist.github.com/Flar49/06b8c9894458a3ff1b14
我希望这个解释和要点能帮助将来遇到同样问题的其他人:)
我有一个应用程序使用 NSTimer 以厘秒(0.01 秒)更新间隔显示 运行 秒表,字符串格式为 00:00.00 (mm:ss.SS)。 (基本上克隆了iOS内置秒表以集成到实时运动计时数学问题中,将来可能需要毫秒精度)
我使用(误用?)NSTimer 来强制更新 UILabel。如果用户按下 Start,这是用于开始重复该功能的 NSTimer 代码:
displayOnlyTimer = NSTimer.scheduledTimerWithTimeInterval(0.01, target: self, selector: Selector("display"), userInfo: nil, repeats: true)
下面是上面的 NSTimer 执行的函数:
func display() {
let currentTime = CACurrentMediaTime() - timerStarted + elapsedTime
if currentTime < 60 {
timeDisplay.text = String(format: "%.2f", currentTime)
}else if currentTime < 3600 {
var minutes = String(format: "%00d", Int(currentTime/60))
var seconds = String(format: "%05.2f", currentTime % 60)
timeDisplay.text = minutes + ":" + seconds
}else {
var hours = String(format: "%00d", Int(currentTime/3600))
var minutes = String(format: "%02d", (Int(currentTime/60)-(Int(currentTime/3600)*60)))
var seconds = String(format: "%05.2f", currentTime % 60)
timeDisplay.text = hours + ":" + minutes + ":" + seconds
}
}
至少会同时显示2个links 运行。当所有其他元素都在起作用时,此方法是否效率太低?
当用户按下 stop/pause/reset 时,显示会在不使用 NSTimer 的情况下更新。我没有找到任何直接翻译成 Swift 的内容。我相当确定我正在使用一种低效的方法来强制快速更新 UIView 中的文本 UILabel。
更多详情: 我正在为 运行 计时器格式 (mm:ss.SS) 编写更简洁的代码。完成后我会再更新一次。
更新:感谢 Rob 和 jtbandes 回答了我的两个问题(格式化方法和显示更新方法)。 用 CADisplayLink():
替换 NSTimer(见上文)很容易displayLink = CADisplayLink(target: self, selector: Selector("display"))
displayLink.addToRunLoop(NSRunLoop.currentRunLoop(), forMode: NSRunLoopCommonModes)
然后替换
代码中的所有实例displayOnlyTimer.invalidate()
与
displayLink.paused = true
(这将暂停显示 link 更新)
要快速 UI 更新,您应该使用 CADisplayLink。任何比显示刷新率快的东西都是对处理能力的浪费,因为它在物理上无法显示。它还提供了前一帧的 timestamp
,因此您可以尝试预测下一帧的时间。
您正在多次计算 CACurrentMediaTime() - timerStarted + elapsedTime
。我建议只做一次并将其保存在局部变量中。
考虑使用 NSDateComponentsFormatter。尝试重用格式化程序的一个实例,而不是每次都创建一个新实例(这通常是最昂贵的部分)。总的来说,字符串操作越少越好。
您可以在显示方法的开始和结束处检查 CACurrentMediaTime,以了解需要多长时间。理想情况下,它应该 much 小于 16.6ms。在 Xcode 调试导航器中密切关注 CPU 使用情况(和一般功耗)。
我今天正在解决同样的问题并找到了这个答案。 Rob 和 jtbandes 的建议很有帮助,我能够 assemble 从 Internet 上找到干净有效的解决方案。谢谢你们。感谢 mothy 的提问。
我决定使用 CADisplayLink,因为没有必要比屏幕更新更频繁地触发计时器的回调:
class Stopwatch: NSObject {
private var displayLink: CADisplayLink!
//...
override init() {
super.init()
self.displayLink = CADisplayLink(target: self, selector: "tick:")
displayLink.paused = true
displayLink.addToRunLoop(NSRunLoop.mainRunLoop(), forMode: NSRunLoopCommonModes)
//...
}
//...
}
我通过每次滴答将 elapsedTime 变量递增 displayLink.duration 来跟踪时间:
var elapsedTime: CFTimeInterval!
override init() {
//...
self.elapsedTime = 0.0
//...
}
func tick(sender: CADisplayLink) {
elapsedTime = elapsedTime + displayLink.duration
//...
}
时间格式化通过 NSDateFormatter 完成:
private let formatter = NSDateFormatter()
override init() {
//...
formatter.dateFormat = "mm:ss,SS"
}
func elapsedTimeAsString() -> String {
return formatter.stringFromDate(NSDate(timeIntervalSinceReferenceDate: elapsedTime))
}
UI 可以在秒表每次报价时调用的回调闭包中更新:
var callback: (() -> Void)?
func tick(sender: CADisplayLink) {
elapsedTime = elapsedTime + displayLink.duration
// Calling the callback function if available
callback?()
}
这就是您在 ViewController 中使用秒表所需要做的全部工作:
let stopwatch = Stopwatch()
stopwatch.callback = self.tick
func tick() {
elapsedTimeLabel.text = stopwatch.elapsedTimeAsString()
}
这里是 Stopwatch 的完整代码和使用指南的要点: https://gist.github.com/Flar49/06b8c9894458a3ff1b14
我希望这个解释和要点能帮助将来遇到同样问题的其他人:)