NSTimer太慢了

Pra*_*hwa -1 time nstimer ios swift

我见过类似的多个问题; 但是,他们都没有解决我的问题.我的计时器太慢了.我只使用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
Run Code Online (Sandbox Code Playgroud)

我也试过使用延迟:

func delay(delay:Double, closure:()->()) {

    dispatch_after(
    dispatch_time( DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC))), dispatch_get_main_queue(), closure)

}
Run Code Online (Sandbox Code Playgroud)

我叫updateTimeviewDidLoad,并在年底updateTime,我说:

delay(0.01) { () -> () in
    self.updateTime()
}
Run Code Online (Sandbox Code Playgroud)

但是,它仍然以与以前相同的速度运行.

我该如何解决这个问题?如果我在研究过程中遗漏了一个问题,请告诉我.谢谢!

rob*_*off 8

这里有一大堆问题.让我们清理一下吧.

class ViewController: UIViewController {

    @IBOutlet var timerLabel: UILabel!
Run Code Online (Sandbox Code Playgroud)

首先,不要使用计时器来更新标签.用一个CADisplayLink.显示链接与屏幕刷新间隔同步,在大多数iOS设备(不是1/100)上是1/60秒,因此您不需要做额外的工作:

    private var displayLink: CADisplayLink?
Run Code Online (Sandbox Code Playgroud)

接下来,不要尝试通过在计时器(或链接)触发时递增计数器来跟踪已用时间,因为计时器或链接不能保证按照您的请求频繁触发.而是存储计时器启动的时间,以及计时器停止的时间(如果它已停止):

    private var startTime: CFAbsoluteTime = 0
    private var endTime: CFAbsoluteTime = 0 {
        didSet {
            updateLabel()
        }
    }
Run Code Online (Sandbox Code Playgroud)

使用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()
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

经过的时间取决于状态,因此添加一个计算方法:

    private var elapsedTime: NSTimeInterval {
        switch state {
        case .Stopped: return endTime - startTime
        case .Running: return CFAbsoluteTimeGetCurrent() - startTime
        case .Pending: return 0
        }
    }
Run Code Online (Sandbox Code Playgroud)

更新标签时,使用格式字符串将经过的时间转换为字符串:

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

更改计时器状态可以更改标签颜色和已用时间,因此在状态更改时更新标签颜色和标签文本:

    private var state = State.Stopped {
        didSet {
            timerLabel.textColor = state.labelColor
            updateLabel()
        }
    }
Run Code Online (Sandbox Code Playgroud)

触摸开始时,根据需要创建显示链接,然后更新状态.州政府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
        }
    }
Run Code Online (Sandbox Code Playgroud)

触摸结束时,如有必要,启动计时器:

    override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
        if state == .Pending {
            startTime = CFAbsoluteTimeGetCurrent()
            displayLink?.paused = false
            state = .Running
        }
    }
Run Code Online (Sandbox Code Playgroud)

以下是创建显示链接的方法:

    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
    }
Run Code Online (Sandbox Code Playgroud)

这是显示链接将调用的方法:

    func displayLinkDidFire(_: CADisplayLink) {
        updateLabel()
    }

} // end of ViewController
Run Code Online (Sandbox Code Playgroud)