简体   繁体   中英

stop/start NSTimer when app goes to background

as the title suggests, i am trying to stop my NSTimer when my game goes to background and start it back when the game starts again. the interruptions obviously could be such as receiving a phone call etc..

i am using the following code in app delegate to stop and restart the scene when the app gets interrupted, but it does not stop the timer.

- (void)applicationWillResignActive:(UIApplication *)application {

    SKView *view = (SKView *)self.window.rootViewController.view;
    view.scene.paused = YES;
}
- (void)applicationDidBecomeActive:(UIApplication *)application {

    SKView *view = (SKView *)self.window.rootViewController.view;
    view.scene.paused = NO;
}

in my scene where i place the timer, i have placed the observers in -(id)initWithSize:(CGSize)size method. here are the observers:

[[NSNotificationCenter defaultCenter]
         addObserver:self
         selector:@selector(goBackground)
         name:UIApplicationDidEnterBackgroundNotification
         object:nil];

[[NSNotificationCenter defaultCenter]
         addObserver:self
         selector:@selector(backBackground)
         name:UIApplicationDidBecomeActiveNotification object:nil];

and the selector methods:

- (void) goBackground {
    [timer invalidate], timer = nil;
}

- (void) backBackground {
    if([timer isValid]) {
        [timer invalidate];
    }
    timer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:@selector(updateCounter:) userInfo:nil repeats:YES];

}

and i am also trying to deallocating the observers as follows:

- (void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillResignActiveNotification object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidBecomeActiveNotification object:nil];
}

the code above gets fired, i have placed an NSLog on all the above methods. but what happens is, it works for scene 1 which is the place for level 1 and when the player wins level 1 and goes to level two and i recreate the app interruption scenario again, it comes back and works for a few seconds before the game crashes.

obviously my code is not working and i have tried every which way i know how to get around that, but to no avail. the timer i have is just to show how much time is left in the level and has nothing to do with the win/lose scenario.(this maybe a bit of extra info which may not be needed to know).

is there anyway that i can stop and restart my timer when my game goes to background? i truly appreciate any help.

i don't know if the timer code is needed but it is as follows:

- (void) gameTimerLabel {
    gameTimerLabel = [SKLabelNode labelNodeWithFontNamed:@"ArialRoundedMTBold"];
   // gameTimerLabel.text = [NSString stringWithFormat:@"%d", _lives];
    gameTimerLabel.fontSize = 20.0;
    gameTimerLabel.fontColor = [SKColor colorWithRed:135.0/255 green:207.0/255 blue:232.0/255 alpha:0.6];
    gameTimerLabel.position = gameTimerLabel.position = CGPointMake(270, 282);
    [self addChild:gameTimerLabel];
}

- (void)updateCounter:(NSTimer *)theTimer {
    if(secondsLeft > 0 ){
        secondsLeft -- ;
        //hours = secondsLeft / 3600;
        minutes = (secondsLeft % 3600) / 60;
        seconds = (secondsLeft %3600) % 60;
        gameTimerLabel.text = [NSString stringWithFormat:@"%02d:%02d", minutes, seconds];
    }
    else{
        secondsLeft = 70;
    }
}

-(void)countdownTimer{

    secondsLeft = hours = minutes = seconds = 0;
    if([timer isValid]) {
        [timer invalidate];
    }
    timer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:@selector(updateCounter:) userInfo:nil repeats:YES];

}

it is basically just a count down timer nothing fancy.

Edit:

Ben-G, was right. there is almost no way to manage NSTimer in background/foreground, so i used the SKAction approach and did get the job done. for those who are interested, here is the working code:

@implementation MyScene {
SKLabelNode *gameTimerLabel;
int countDownInt;
    int secondsLeft;
}

-(id)initWithSize:(CGSize)size {
    if (self = [super initWithSize:size]) {
secondsLeft = 70;
[self gameTimerLabel];
}
    return self;
}

- (void) gameTimerLabel {
    gameTimerLabel = [SKLabelNode labelNodeWithFontNamed:@"ArialRoundedMTBold"];
    gameTimerLabel.fontSize = 20.0;
    gameTimerLabel.fontColor = [SKColor colorWithRed:135.0/255 green:207.0/255 blue:232.0/255 alpha:0.6];
    gameTimerLabel.position = gameTimerLabel.position = CGPointMake(270, 282);
    [self addChild:gameTimerLabel];
    countDownInt = secondsLeft;
    SKAction *updateLabel = [SKAction runBlock:^{
        gameTimerLabel.text = [NSString stringWithFormat:@" %02d", countDownInt];
        --countDownInt;
    }];
    SKAction *wait = [SKAction waitForDuration:1.0];
    SKAction *updateLabelAndWait = [SKAction sequence:@[updateLabel, wait]];
    [self runAction:[SKAction repeatAction:updateLabelAndWait count:secondsLeft] completion:^{
        gameTimerLabel.text = @"Time's Up";
    }];
}

thats all. thanks again to Ben-G for pointing me in the right direction.

Even though it doesn't answer your question exactly, I would refer to the accepted answer of this question: SpriteKit - Creating a timer .

In general you want to avoid using NSTimer when working with a game engine. Your timed actions will not be integrated with the game loop and your timers won't be stopped automatically when the game is paused.

Instead integrate with the update method or use an SKAction .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM