32

I'm developing a game and I want to create a pause menu. Here is my code:

self.view?.paused = true

but NSTimer.scheduledTimerWithTimeInterval still running...

 for var i=0; i < rocketCount; i++ {
    var a: NSTimeInterval = 1
    ii += a
    delaysShow = 2.0 + ((stimulus + interStimulus) * ii)       
    var time3 = NSTimer.scheduledTimerWithTimeInterval(delaysShow!, target: self, selector: Selector("showRocket:"), userInfo: rocketid[i], repeats: false)
 }

I want time3 to pause the timer when player click pause menu and continue run the timer when player come back to the game, but how can I pause NSTimer.scheduledTimerWithTimeInterval? help me please.

Ratnesh Jain
  • 671
  • 7
  • 14
Pandu Arif Septian
  • 340
  • 1
  • 3
  • 5
  • 1
    Just to clarify, u want to pause an existing timer when a "pause" button is pressed....and u want to resume the timer when the "resume" button is pressed......correcT? – Karthik Mar 17 '16 at 07:24

9 Answers9

54

You need to invalidate it and recreate it. You can then use an isPaused bool to keep track of the state if you have the same button to pause and resume the timer:

var isPaused = true
var timer = NSTimer()    
@IBAction func pauseResume(sender: AnyObject) {     
    if isPaused{
        timer = NSTimer.scheduledTimerWithTimeInterval(0.1, target: self, selector: Selector("somAction"), userInfo: nil, repeats: true)
        isPaused = false
    } else {
        timer.invalidate()
        isPaused = true
    }
}
pjtnt11
  • 495
  • 2
  • 5
  • 22
jonnie
  • 12,260
  • 16
  • 54
  • 91
  • @PanduArifSeptian this should be the accepted answer if you are looking for what works with Swift 2. It works for me. – Kent Bull Nov 09 '15 at 01:00
9

To Start:

timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: Selector("updateView"), userInfo: nil, repeats: true)

To Resume:

timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: Selector("updateView"), userInfo: nil, repeats: true)

To Pause:

timer.invalidate

This worked for me. The trick is that do not look for something like "timer.resume" or "timer.validate". Just use "the same code for starting a timer" to resume it after the pause.

Sushil
  • 2,837
  • 4
  • 21
  • 29
Ancalagon BerenLuthien
  • 1,154
  • 6
  • 20
  • 29
9

To start

timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(ViewController.action), userInfo: nil, repeats: true)

To pause

timer.invalidate()

To reset

time += 1
label.text = String(time)

'label' is the timer on output.

Rashid KC
  • 737
  • 1
  • 9
  • 17
9

I was just working through a similar problem with my game, and found an easy solution.

First I should point out like others have, that Timer and NSTimer doesn't have a pause function. You have to stop the Timer with Timer.invalidate(). After invalidating a Timer, you must initialize it again to start the Timer. To quote from https://developer.apple.com/documentation/foundation/timer, the function .invalidate() -

Stops the timer from ever firing again and requests its removal from its run loop.


To pause a timer we can use Timer.fireDate, this is where Timer (and NSTimer) saves the date for when the Timer will fire in the future.

Here's how we can pause a Timer by saving the seconds left that the Timer has until it fires again.

//The variable we will store the remaining timers time in
var timeUntilFire = TimeInterval()

//The timer to pause
var gameTimer = Timer.scheduledTimer(timeInterval: delaysShow!, target: self, selector: #selector(GameClass.showRocket), userInfo: rocketid[i], repeats: false)

func pauseTimer()
{
    //Get the difference in seconds between now and the future fire date
    timeUntilFire = gameTimer.fireDate.timeIntervalSinceNow
    //Stop the timer
    gameTimer.invalidate()
}

func resumeTimer()
{
    //Start the timer again with the previously invalidated timers time left with timeUntilFire
    gameTimer = Timer.scheduledTimer(timeInterval: timeUntilFire, target: self, selector: #selector(GameClass.showRocket), userInfo: rocketid[i], repeats: false)
}

Note: Don't invalidate() the Timer before getting the fireDate. After invalidate() is called the Timer seems to reset the fireDate to 2001-01-01 00:00:00 +0000.

2nd Note: A timer can potentially fire after its set fireDate. This will lead to a negative number, which will default the Timer to run after 0.1 milliseconds instead. https://developer.apple.com/documentation/foundation/timer/1412416-scheduledtimer

stelf
  • 91
  • 1
  • 2
4

To stop it

  time3.invalidate() 

To start it again

  time3.fire()
  • Check this answer http://stackoverflow.com/questions/9975562/how-to-pause-play-nstimer – Christos Chadjikyriacou Apr 02 '15 at 09:00
  • @Christos Hadjikyriacou When I put fire nothing happens. Any help? – Anton O. Jan 06 '16 at 03:10
  • 1
    I think you should re-initialise the NSTimer. – Christos Chadjikyriacou Jan 11 '16 at 07:26
  • 13
    From the Apple docs: "(invalidate) Stops the receiver from ever firing again and requests its removal from its run loop." Once calling invalidate, you should set the timer to nil and (re)instantiate/fire again to "restart". – preynolds May 03 '16 at 21:59
  • 2
    `.fire()` does not start the timer again (in the sense that it will continue to repeat the call to the selector.) In order to do that (which is what the OP meant by "resume") you must reinitialize the timer. – Clay Ellis Aug 17 '16 at 21:48
4

You can not resume the timer back. Instead of resuming - just create a new timer.

class SomeClass : NSObject { // class must be NSObject, if there is no "NSObject" you'll get the error at runtime

    var timer = NSTimer()

    init() {
        super.init()
        startOrResumeTimer()
    }

    func timerAction() {
        NSLog("timer action")
    }

    func pauseTimer() {
        timer.invalidate
    }

    func startOrResumeTimer() {
        timer = NSTimer.scheduledTimerWithTimeInterval(0.5, target: self, selector: Selector("timerAction"), userInfo: nil, repeats: true)
    }
}
nickeyzzz
  • 355
  • 4
  • 13
3

Even though the solutions exposed here are good, I thought an important insight was missing. Like a lot of people here explained, timer invalidate() and recreate is the best option. But one could argue that you could do something like this:

var paused:Bool

func timerAction() {
    if !paused {
        // Do stuff here
    }
}

is easier to implement, but it will be less efficient.

For energy impact reasons, Apple promotes avoiding timers whenever possible and to prefer event notifications. If you really need to use a timer, you should implement pauses efficiently by invalidating the current timer. Read recommendations about Timer in the Apple Energy Efficiency Guide here: https://developer.apple.com/library/content/documentation/Performance/Conceptual/EnergyGuide-iOS/MinimizeTimerUse.html

Béatrice Cassistat
  • 1,048
  • 12
  • 37
2

SWIFT3

Global Declaration :

 var swiftTimer = Timer()
 var count = 30
 var timer = Timer()
 @IBOutlet weak var CountDownTimer: UILabel!

viewDidLoad

override func viewDidLoad() { super.viewDidLoad() BtnStart.tag = 0 }

Triggered IBACTION :

@IBAction func BtnStartTapped(_ sender: Any) {
      if BtnStart.tag == 0 {
           BtnStart.setTitle("STOP", for: .normal)
           timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(ScoreBoardVC.update), userInfo: nil, repeats: true)

           BtnStart.tag = 1
      } else {

           BtnStart.setTitle("START", for: .normal)
           timer.invalidate()

           BtnStart.tag = 0
      }                    
 }

Function that Handles The things :

func update(){

      if(count > 0){
           let minutes = String(count / 60)
           let ConvMin = Float(minutes)
           let minuttes1 = String(format: "%.0f", ConvMin!)

           print(minutes)
           let seconds = String(count % 60)
           let ConvSec = Float(seconds)
           let seconds1 = String(format: "%.0f", ConvSec!)

           CountDownTimer.text = (minuttes1 + ":" + seconds1)
           count += 1
      }          
 }
Elio Lako
  • 1,333
  • 2
  • 16
  • 26
Azharhussain Shaikh
  • 1,654
  • 14
  • 17
0

Pause timer : timer.invalidate()

and

Resume timer : recreate timer. it's work fine.

timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(mainController.updateTimer), userInfo: nil, repeats: true)
Chetan Lodhi
  • 353
  • 1
  • 3
  • 21