14

I tried to search everywhere in the code:Explained documentry what it does when going to background, or if it is even paused sometime, but to no avail- can someone direct me in the way of what is recommended to do when going to background in sprite kit enabled game?

Should I just call scene.paused = YES, or how can I confirm that no drawing occurs in background so I can avoid termination by iOS which won't allow me that?

Thanks!

Dominique Vial
  • 3,729
  • 2
  • 25
  • 45
Lior Pollak
  • 3,362
  • 5
  • 27
  • 48

6 Answers6

40

SpriteKit really isn't documented that well yet, probably because it's so new that relatively few developers have implemented it. SpriteKit should pause animations when backgrounding, but in my experience (and harrym17's) it will cause memory access errors and quits the app entirely rather than backgrounding it.

According to the Apple Developer forums, it seems this is a known bug in SpriteKit (it doesn't always pause animations when backgrounding, as it should) causing memory access errors as per harrym17's comment. Here's a brief fix which has worked for me - my app was consistently crashing when backgrounding, and since adding this code everything works fine.

(Kudos to Keith Murray for this code on the Apple forums; as far as I can see he hasn't posted it on SO).

In AppDelegate.h, make sure you import SpriteKit:

#import <SpriteKit/SpriteKit.h>

In AppDelegate.m, edit your applicationWillResignActive method:

- (void)applicationWillResignActive:(UIApplication *)application
{
     // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
     // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.

     // pause sprite kit
     SKView *view = (SKView *)self.window.rootViewController.view;
     view.paused = YES;
}

And this to your applicationDidBecomeActive method:

- (void)applicationDidBecomeActive:(UIApplication *)application
{
     // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.

     // resume sprite kit
     SKView *view = (SKView *)self.window.rootViewController.view;
     view.paused = NO;
}

There are additional problems when backgrounding when playing audio via AVAudioSession, apparently; a workaround is mentioned in this thread.

Community
  • 1
  • 1
MassivePenguin
  • 3,701
  • 4
  • 24
  • 46
  • This doesn't prevent the crash (for me at least) – Lior Pollak Oct 12 '13 at 10:26
  • Excellent. I can confirm that this works! This should be added by default in the AppDelegate for any Sprite Kit app if you ask me.. Also, if you use sounds in your app, combine it with the AVAudioSession fix in this same topic. Thanks – stvn Feb 25 '14 at 20:55
  • I haven't tried it in this context, but you may be able to use a NSTimer (see http://stackoverflow.com/questions/1449035/how-do-i-use-nstimer). I *have* used an NSTimer to set a different variable when the app becomes active, so it should work for you. – MassivePenguin Mar 10 '14 at 08:01
  • i'm trying this code with iOS 7.1 and the app crashes on start ever – Ilario Mar 25 '14 at 11:48
  • It's still working fine for me (on 7.1). What error are you getting, and else do you have in your code that could cause the issue? – MassivePenguin Mar 25 '14 at 12:09
  • @MassivePenguin thanks for your answer,this happen on launch of the app, this is the crash code: *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UILayoutContainerView setPaused:]: unrecognized selector sent to instance 0x16548990' – Ilario Mar 25 '14 at 12:37
  • Do you have iAd (or another subview) running? http://stackoverflow.com/questions/19014012/sprite-kit-the-right-way-to-multitask/19311794?noredirect=1#answer-21951898 – MassivePenguin Mar 25 '14 at 12:40
  • at start no .. i solved with this answer http://stackoverflow.com/questions/22299905/ios-spritekit-multitasking-issue?lq=1 – Ilario Mar 25 '14 at 12:46
  • 1
    I love you @MassivePenguin! Been having trouble with this issue for a few days, and you're solution fixed my woes. So happy! – sixtysticks May 09 '14 at 09:05
  • It is not working for me on iOS8. Did not try on iOS7. – Ramis Sep 21 '14 at 12:08
  • iOS 8 seems to have squashed many Sprite Kit bugs (and doubtless introduced many more). In an empty Sprite Kit project running Swift, you should no longer experience the Memory Access Errors as described above when backgrounding the app; if you do, there are likely other causes (such as `AudioSession` or iAds). Look through the other responses for help with those. – MassivePenguin Sep 22 '14 at 09:00
  • After updating to 8.3 this isn't working for me any more, with no other code changes on my end. Been banging my head against it all day, but don't see anything in the change logs that seem to imply the difference. Any suggestions? – fith Apr 17 '15 at 21:46
  • Is this legacy code, or a new app? I've migrated to Swift and haven't come across any backgrounding issues. – MassivePenguin Apr 20 '15 at 10:05
14

As said here by LearnCocos2D:

The problem is AVAudioSession can't be active while the app enters background.

The fix is quite simple, and also applies to ObjectAL => set the AVAudioSession to inactive while the app is in background, and reactivate the audio session when the app enters foreground.

A simplified AppDelegate with this fix looks like so:

#import <AVFoundation/AVFoundation.h>

...

- (void)applicationWillResignActive:(UIApplication *)application
{
    // prevent audio crash
    [[AVAudioSession sharedInstance] setActive:NO error:nil];
}

- (void)applicationDidEnterBackground:(UIApplication *)application
{
    // prevent audio crash
    [[AVAudioSession sharedInstance] setActive:NO error:nil];
}

- (void)applicationWillEnterForeground:(UIApplication *)application
{
    // resume audio
    [[AVAudioSession sharedInstance] setActive:YES error:nil];
}

PS: this fix will be included in Kobold Kit v7.0.3.

Community
  • 1
  • 1
Lior Pollak
  • 3,362
  • 5
  • 27
  • 48
10

I was wondering why in the world this seemingly simple solution was causing me crashes on app startup, but it was because I had iAd running. So, a thing to keep in mind: @MassivePenguin's answer works but if you have iAd going you will have to grab the SKView via the subviews or it (obviously) crashes.

-(SKView*)getSKViewSubview{
    for (UIView* s in self.window.rootViewController.view.subviews) {
        if ([s isKindOfClass:[SKView class]]) {
            return (SKView*)s;
        }
    }
    return nil;
}

- (void)applicationWillResignActive:(UIApplication *)application {    
    SKView* view = [self getSKViewSubview];

    if (view) {
        view.paused = YES;
    }

}

- (void)applicationDidBecomeActive:(UIApplication *)application
{
    SKView* view = [self getSKViewSubview];

    if (view) {
        view.paused = NO;
    }

}
revenantkioku
  • 111
  • 1
  • 8
1

For me did not work none of one provided solutions. I did found another way how to do it.

// In the AppDelegate
- (void)applicationWillResignActive:(UIApplication *)application
{
    if ([self.window.rootViewController isKindOfClass:[GameViewController class]]) {
        GameViewController *vc = (GameViewController*)self.window.rootViewController;
        [vc pauseGame];
        [vc.view addObserver:vc
                  forKeyPath:@"paused"
                     options:NSKeyValueObservingOptionNew
                     context:nil];
    }
}

// In the GameViewController
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    SKView *skView = (SKView*)self.view;
    MainGame *mainGame = (MainGame*)skView.scene;
    if ([keyPath isEqualToString:@"paused"] &&
        [[change objectForKey:NSKeyValueChangeNewKey] boolValue] == NO &&
        [mainGame isGameInProgress]) {
        [self.view removeObserver:self forKeyPath:@"paused" context:nil];
        skView.paused = YES;
    }
}
Ramis
  • 13,985
  • 7
  • 81
  • 100
0

iPad seems to background ok, but iPhone definitely crashes when backgrounding a sprite kit game. You have to do this manually, the automagical piece isn't so automagical.

Justin Donohoo
  • 380
  • 2
  • 14
-4

Sprite Kit automatically pauses its animation timers when moving to the background -- you don't need to worry about your app getting killed for that.

rickster
  • 124,678
  • 26
  • 272
  • 326
  • Yet apparently iOS terminated my app, something due to EXC_BAD_ACCESS but is also showing gpus_ReturnNotAllowedKillClient? – Lior Pollak Sep 27 '13 at 10:29
  • Sprite Kit _says_ that it automatically pauses its animation timers but there are definitely problems with this. – Kardasis Oct 01 '13 at 15:45
  • I was getting the `EXEC_BAD_ACCESS` error on backgrounding too - the code in my answer _seems_ to have fixed it, although my app really isn't that complex at the moment... – MassivePenguin Oct 11 '13 at 06:55