swifttimerios8nstimerinvalidation

ios8 Swift SpriteKit - Pause and Resume NSTimers in swift


I have searched many times on the internet but could not find the answer to this question. I know how to pause and resume NSTimers by using the invalidate functions - timer.invalidate. and I know how to resume them. But I have a SpriteKit game. When I pause my game, I stop everything and the timers. I know that I can stop them using .invalidate but when I invalidate them:

For example lets say I have a 5 second timer that runs continously that spawns one block.

When the timer reaches second 3 of the cycle and when I paused the game, and invalidate the timers. When I resume, Now the timers second goes back to 0 and I must wait another 5 seconds. I want it to continue from where it left off, 3 , and wait 2 seconds for the block to spawn.

            blockGenerator.generationTimer?.invalidate()

            self.isGamePaused = true
            self.addChild(self.pauseText)

            self.runAction(SKAction.runBlock(self.pauseGame))

e`

and when I resume it:

blockGenerator.generationTimer = ...

I have to wait another 5 seconds, I want the timer to continue from where it left off

If you can help me, I appreciate it thank you.


Solution

  • There is a way to pause/resume Timer instances, because using repeating timers we know the next fire date.

    This is a simple class SRTimer and a protocol SRTimerDelegate


    Protocol SRTimerDelegate

    protocol SRTimerDelegate : AnyObject {
        func timerWillStart(_ timer : SRTimer)
        func timerDidFire(_ timer : SRTimer)
        func timerDidPause(_ timer : SRTimer)
        func timerWillResume(_ timer : SRTimer)
        func timerDidStop(_ timer : SRTimer)
    }
    

    Class SRTimer

    class SRTimer : NSObject {
        
        var timer : Timer?
        var interval : TimeInterval
        var difference : TimeInterval = 0.0
        var delegate : SRTimerDelegate?
        
        init(interval: TimeInterval, delegate: SRTimerDelegate?)
        {
            self.interval = interval
            self.delegate = delegate
        }
    
        @objc func start(_ aTimer : Timer?)
        {
            if aTimer != nil { fire(self) }
            if timer == nil {
                delegate?.timerWillStart(self)
                timer = Timer.scheduledTimer(timeInterval: interval, target: self, selector: #selector(fire), userInfo: nil, repeats: true)
            }
        }
    
        func pause()
        {
            if timer != nil {
                difference = timer!.fireDate.timeIntervalSince(Date())
                timer!.invalidate()
                timer = nil
                delegate?.timerDidPause(self)
            }
        }
    
        func resume()
        {
            if timer == nil {
                delegate?.timerWillResume(self)
                if difference == 0.0 {
                    start(nil)
                } else {
                    Timer.scheduledTimer(timeInterval: difference, target: self, selector: #selector(start), userInfo: nil, repeats: false)
                    difference = 0.0
                }
            }
        }
    
        func stop()
        {
            if timer != nil {
                difference = 0.0
                timer!.invalidate()
                timer = nil
                delegate?.timerDidStop(self)
            }
        }
        
        @objc func fire(_ sender : SRTimer)
        {
            delegate?.timerDidFire(self)
        }
    
    }
    

    Make your class conform to the protocol SRTimerDelegate and initialize a SRTimer instance with

    var timer : SRTimer!
    timer = SRTimer(interval: 5.0, delegate: self)
    

    Methods

    start() calls the delegate method timerWillStart and starts the timer.

    pause() saves the difference between the current date and the next fire date, invalidates the timer and calls the delegate method timerDidPause.

    resume() calls the delegate method timerWillResume, creates a temporary one shot timer with the saved difference time interval. When this timer fires the main timer will be restarted.

    stop() calls the delegate method timerDidStop and invalidates the timer.

    When the timer fires, the delegate method timerDidFire is called.