NSTimer太慢了

我看到过类似的问题。 然而,他们没有一个解决了我的问题。 我的计时器太慢了。 我只用了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) } 

我在viewDidLoad调用了updateTime ,在updateTime的末尾添加了:

 delay(0.01) { () -> () in self.updateTime() } 

不过,它仍然和以前一样。

我该如何解决这个问题? 如果我在研究的时候错过了一个问题,请告诉我。 谢谢!

这里有一大堆问题。 我们来清理一下。

 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 } } 

更新标签时,使用格式string将已用时间转换为string:

  private func updateLabel() { timerLabel.text = String(format: "%.02f", elapsedTime) } 

改变计时器状态可以改变标签颜色和经过时间,所以当状态改变时更新标签颜色和标签文本:

  private var state = State.Stopped { didSet { timerLabel.textColor = state.labelColor updateLabel() } } 

触摸开始时,根据需要创build显示链接,然后更新状态。 状态的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 } } 

以下是您如何创build显示链接:

  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 

从我上面的评论…(鼓励)

NSTimer – 来自苹果开发者文档:

“一个计时器不是一个实时机制,只有在计时器已经join的一个运行循环模式正在运行,并且能够检查计时器的开始时间是否已经过去时,它才会触发。由于不同的input源,一个典型的运行循环pipe理,定时器的时间间隔的有效分辨率被限制在50-100毫秒左右。