Pause NSTimer on home-button-press and start them

2019-05-24 06:04发布

问题:

I've been searching for a solution to pause my SpriteKit game when the user "tabs down" the game. So far I found a solution where you use SKAction's instead of NSTimer's, this works as long as the time between actions stays the same. However, my NSTimer's changes in speed. So I need to find another solution.

I have a bunch of NSTimer's located in GameScene -> didMoveToView

NSTimer.scheduledTimerWithTimeInterval(0.2, target: self, selector: Selector("SpawnBullets"), userInfo: nil, repeats: true)

NSTimer.scheduledTimerWithTimeInterval(0.1, target: self, selector: Selector("SpawnMeteors"), userInfo: nil, repeats: true)

NSTimer.scheduledTimerWithTimeInterval(timeInterval, target: self, selector: Selector("onTimer:"), userInfo: nil, repeats: true)

Now, how would I simply pause them when the app enters background?

EDIT: Added my timeInterval increase-speed-function

func onTimer(timer: NSTimer) {
    var goodTimes = time / 20

    if (goodTimes > 1.8){
        goodTimes = 1.8
    }

    timer.fireDate = timer.fireDate.dateByAddingTimeInterval(timeInterval - goodTimes)

    self.runAction(SKAction.sequence([SKAction.runBlock(SpawnRocks), SKAction.waitForDuration(goodTimes / 2), SKAction.runBlock(SpawnPowerUp)]))
}

回答1:

Pausing of NSTimer is not a native feature in objective-c or swift. To combat this, you need to create an extension, which I happen to have created and will share for you. This will work for both OS X and iOS

import Foundation
import ObjectiveC

#if os(iOS)
    import UIKit
#else
    import AppKit
#endif


private var pauseStartKey:UInt8 = 0;
private var previousFireDateKey:UInt8 = 0;

extension NSTimer
{
    private var pauseStart: NSDate!{
        get{
            return objc_getAssociatedObject(self, &pauseStartKey) as? NSDate;

        }
        set(newValue)
        {
            objc_setAssociatedObject(self, &pauseStartKey,newValue,objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN);
        }
    }

    private var previousFireDate: NSDate!{
        get{
            return objc_getAssociatedObject(self, &previousFireDateKey) as? NSDate;

        }
        set(newValue)
        {
            objc_setAssociatedObject(self, &previousFireDateKey,newValue,objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN);
        }
    }


    func pause()
    {
        pauseStart = NSDate();
        previousFireDate = self.fireDate;
        self.fireDate = NSDate.distantFuture() ;
    }

    func resume()
    {
        if(pauseStart != nil)
        {
            let pauseTime = -1 * pauseStart.timeIntervalSinceNow;
            let date = NSDate(timeInterval:pauseTime, sinceDate:previousFireDate );
            self.fireDate = date;
        }

    }
}

Then when you need to use it, simply call timer.pause() and timer.resume() You of course have to keep track of your timers in your gamescene object to do this, so make sure that timer is a variable accessible for the entire class, and when making your timer, you do timer = NSTimer.schedule...

At the beginning of your class:

var spawnBulletsTimer : NSTimer?;
var spawnMeteorsTimer : NSTimer?;
var onTimer: NSTimer?;

When creating the timers:

spawnBulletsTimer = NSTimer.scheduledTimerWithTimeInterval(0.2, target:     self, selector: Selector("SpawnBullets"), userInfo: nil, repeats: true)

spawnMeteorsTimer = NSTimer.scheduledTimerWithTimeInterval(0.1, target: self, selector: Selector("SpawnMeteors"), userInfo: nil, repeats: true)

onTimer = NSTimer.scheduledTimerWithTimeInterval(timeInterval, target: self, selector: Selector("onTimer:"), userInfo: nil, repeats: true)

Then when you need to pause:

onTimer?.pause() 
spawnBulletsTimer?.pause()
spawnMeteorTimer?.pause()

Then when you need to resume:

onTimer?.resume() 
spawnBulletsTimer?.resume()
spawnMeteorTimer?.resume()