我见过类似的多个问题;但是,他们都没有解决我的问题。我的计时器太慢了。我只使用0.01
秒的间隔。这是我的代码:
@IBOutlet var timerLabel: UILabel!
var miliseconds = 0
var seconds = 0
func updateLabel() {
if miliseconds == 0 {
timerLabel.text = "\(seconds).00"
} else if miliseconds < 10 {
timerLabel.text = "\(seconds).0\(miliseconds)"
} else {
timerLabel.text = "\(seconds).\(miliseconds)"
}
}
var timer = NSTimer()
func updateTime() {
miliseconds++
if miliseconds == 100 {
miliseconds = 0
seconds++
}
updateLabel()
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
if timerState == 1 {
timer = NSTimer.scheduledTimerWithTimeInterval(0.01, target: self, selector: "updateTime", userInfo: nil, repeats: true)
NSRunLoop.mainRunLoop().addTimer(timer, forMode: NSRunLoopCommonModes)
timerLabel.textColor = UIColor.blackColor()
timerState = 2
}
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
if timerState == 0 {
miliseconds = 0
seconds = 0
updateLabel()
timerLabel.textColor = UIColor.greenColor()
timerState = 1
} else if timerState == 2 {
timerState = 0
timer.invalidate()
}
}
var timerState = 0
//timerState of 0 = Has not started
//timerState of 1 = About to start
//timerState of 2 = Timing
我也试过使用延迟:
func delay(delay:Double, closure:()->()) {
dispatch_after(
dispatch_time( DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC))), dispatch_get_main_queue(), closure)
}
我在updateTime
中致电viewDidLoad
,在updateTime
结束时,我补充道:
delay(0.01) { () -> () in
self.updateTime()
}
然而,它仍然以与以前相同的速度前进。
如何解决此问题?如果我在研究过程中遗漏了一个问题,请告诉我。谢谢!
答案 0 :(得分:7)
这里有一大堆问题。让我们清理一下。
class ViewController: UIViewController {
@IBOutlet var timerLabel: UILabel!
首先,不要使用计时器来更新标签。使用CADisplayLink
。显示链接与屏幕刷新间隔同步,在大多数iOS设备(不是1/100)上是1/60秒,因此您不需要做额外的工作:
private var displayLink: CADisplayLink?
接下来,不要在计时器(或链接)触发时通过递增计数器来跟踪已用时间,因为计时器或链接不能保证按照您的请求频繁触发。而是存储计时器启动的时间,以及计时器停止的时间(如果它已停止):
private var startTime: CFAbsoluteTime = 0
private var endTime: CFAbsoluteTime = 0 {
didSet {
updateLabel()
}
}
使用enum
而非神秘的硬编码数字跟踪状态。并且由于标签颜色仅取决于状态,因此将属性添加到为该状态提供标签颜色的状态:
private enum State {
case Stopped
case Pending
case Running
var labelColor: UIColor {
switch self {
case .Pending: return UIColor.greenColor()
case .Stopped, .Running: return UIColor.blackColor()
}
}
}
经过的时间取决于状态,因此添加计算方法:
private var elapsedTime: NSTimeInterval {
switch state {
case .Stopped: return endTime - startTime
case .Running: return CFAbsoluteTimeGetCurrent() - startTime
case .Pending: return 0
}
}
使用格式字符串将更新标签时经过的时间转换为字符串:
private func updateLabel() {
timerLabel.text = String(format: "%.02f", elapsedTime)
}
更改计时器状态可以更改标签颜色和经过时间,因此在状态更改时更新标签颜色和标签文本:
private var state = State.Stopped {
didSet {
timerLabel.textColor = state.labelColor
updateLabel()
}
}
触摸开始时,根据需要创建显示链接,然后更新状态。州didSet
将根据需要处理更新标签:
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
createDisplayLinkIfNeeded()
switch state {
case .Stopped:
state = .Pending
case .Pending:
break
case .Running:
state = .Stopped
endTime = CFAbsoluteTimeGetCurrent()
displayLink?.paused = true
}
}
触摸结束时,如有必要,启动计时器:
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
if state == .Pending {
startTime = CFAbsoluteTimeGetCurrent()
displayLink?.paused = false
state = .Running
}
}
以下是创建显示链接的方法:
private func createDisplayLinkIfNeeded() {
guard self.displayLink == nil else { return }
let displayLink = CADisplayLink(target: self, selector: "displayLinkDidFire:")
displayLink.paused = true
displayLink.addToRunLoop(NSRunLoop.mainRunLoop(), forMode: NSRunLoopCommonModes)
self.displayLink = displayLink
}
以下是显示链接将调用的方法:
func displayLinkDidFire(_: CADisplayLink) {
updateLabel()
}
} // end of ViewController
答案 1 :(得分:0)
从我上面的评论中......(鼓励)
NSTimer - 来自apple developer docs:
“计时器不是实时机制;只有当其中一个计时器触发时它才会触发 已添加计时器的运行循环模式正在运行且能够运行 检查计时器的开火时间是否已经过去。因为各种各样 输入源一个典型的运行循环管理,有效的分辨率 计时器的时间间隔限制在50-100的量级 毫秒。 “