Stop and restart a timer

强颜欢笑 提交于 2019-12-05 07:21:24

问题


I want to stop this timer and then restart it from where I stopped it.

secondsTimer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(addSeconds), userInfo: nil, repeats: true)

Below, it was suggested I shouldn't increment a timer in my timer handler. Why not?

For example, using GCD timer:

func countSeconds() {

    secondsTimer = DispatchSource.makeTimerSource(queue: .main)
    secondsTimer?.schedule(deadline: .now(), repeating: 1.0)

    secondsTimer?.setEventHandler { [weak self] in

        self?.addSeconds()
    }
}

@objc func addSeconds() {
    seconds += 1                         
}

func startGame() {  
    secondsTimer?.resume()
}

回答1:


We don't pause/resume Timer instances. We stop them with invalidate(). And when you want to restart it, just create new timer.

Please refer to the Timer documentation, also available right in Xcode.


Note that you can suspend and resume GCD timers, DispatchSourceTimer.

var timer: DispatchSourceTimer?  // note, unlike `Timer`, we have to maintain strong reference to GCD timer sources

func createTimer() {
    timer = DispatchSource.makeTimerSource(queue: .main)
    timer?.schedule(deadline: .now(), repeating: 1.0)

    timer?.setEventHandler { [weak self] in      // assuming you're referencing `self` in here, use `weak` to avoid strong reference cycles
        // do something
    }

    // note, timer is not yet started; you have to call `timer?.resume()`
}

func startTimer() {
    timer?.resume()
}

func pauseTiemr() {
    timer?.suspend()
}

func stopTimer() {
    timer?.cancel()
    timer = nil
}

Please note, I am not suggesting that if you want suspend and resume that you should use GCD DispatchSourceTimer. Calling invalidate and recreating Timer as needed is simple enough, so just do that. I only provide this GCD information for the sake of completeness.


By the way, as a general principle, never "increment" some counter in your timer handler. That's a common mistake. Timers are not guaranteed to fire every time or with exact precision. Always save some reference time at the start, and then in your event handler, calculate differences between the current time and the start time. For example, extending my GCD timer example:

func createTimer() {
    timer = DispatchSource.makeTimerSource(queue: .main)
    timer?.schedule(deadline: .now(), repeating: 0.1)

    let formatter = DateComponentsFormatter()
    formatter.unitsStyle = .positional
    formatter.allowedUnits = [.hour, .minute, .second, .nanosecond]
    formatter.zeroFormattingBehavior = .pad

    timer?.setEventHandler { [weak self] in
        guard let start = self?.start else { return }
        let elapsed = (self?.totalElapsed ?? 0) + CACurrentMediaTime() - start
        self?.label.text = formatter.string(from: elapsed)
    }
}

var start: CFTimeInterval?         // if nil, timer not running
var totalElapsed: CFTimeInterval?

@objc func didTapButton(_ button: UIButton) {
    if start == nil {
        startTimer()
    } else {
        pauseTimer()
    }
}

private func startTimer() {
    start = CACurrentMediaTime()
    timer?.resume()
}

private func pauseTimer() {
    timer?.suspend()
    totalElapsed = (totalElapsed ?? 0) + (CACurrentMediaTime() - start!)
    start = nil
}



回答2:


You can declare the Timer as 'weak var' instead of just 'var' like:

weak var timer: Timer?

Now you can pause your timer with:

timer?.invalidate()

To resume:

timer?.fire()


来源:https://stackoverflow.com/questions/46792661/stop-and-restart-a-timer

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!