Reputation: 2854
I find that tapping on iAds can freeze the screen on any Sprite Kit game. It's not just my specific project, since the stock Sprite Kit example project also freezes with iAd. This does not happen in the simulator though! I cannot decide whether if it's because the simulator runs iOS 8 and my actual testing device is on 7.1, or because of the fact that the simulator is just a simulator so it does things differently.
So if you tap on the iAd then click on the link in the iAd to go to safari (or manually switch over to any app at this point), then switch back to the Sprite Kit app, the app is frozen. The iAd banner is still live, and it loads ads just how it's supposed to. But the rest of the app is frozen. Or to be specific, It still receives touches and stuff (I can see from NSLog
s) but the rendering of the nodes is frozen. If you open up iAd again by tapping on it, and close the iAd, then the app resumes somehow and it works again just fine.
If you're curious, here are the ONLY modifications that I did to the stock Sprite Kit example project:
// in GameViewController.h
#import <iAd/iAd.h>
@interface GameViewController : UIViewController <ADBannerViewDelegate>
@end
// in GameViewController.m
@interface GameViewController() {
ADBannerView* iAdBanner;
NSLayoutConstraint* centerAd;
}
@end
@implementation GameViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Configure iAd
iAdBanner = [[ADBannerView alloc] initWithFrame:CGRectZero];
iAdBanner.alpha = 0.0;
iAdBanner.delegate = self;
centerAd = [NSLayoutConstraint constraintWithItem:iAdBanner
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0.0];
[self.view addSubview:iAdBanner];
[self.view addConstraint:centerAd];
// The rest of viewDidLoad is the stock code. I'm not pasting that in...
}
// iAd delegate methods
-(void) bannerViewDidLoadAd:(ADBannerView *)banner {
// fade in iAd banner
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.5];
[banner setAlpha:1.0];
[UIView commitAnimations];
}
-(void) bannerView:(ADBannerView *)banner didFailToReceiveAdWithError:(NSError *)error {
NSLog(@"iAd error: %@", error.localizedDescription);
banner.alpha = 0.0;
}
Plus I set the deployment target to 7.1 (and obviously include the iAd framework in the project).
As you can see, I'm not doing anything to the app that could cause this crash. I simply add the iAd banner and that's it. So the problem is not in my code. This must be a bug in Apple's frameworks.
Does anyone know a workaround? I have found several topics on this on the internet, but nobody could suggest a working solution.
EDIT:
I found out that iOS 7.1 on my device calls bannerViewActionDidFinish:
when returning to the app. iOS 8 in the simulator however, calls that method before leaving the app (exactly when you leave the app to be exact). That doesn't directly affect the code posted above, since it doesn't implement this method. But it does indicate that the iAd implementations iOS 7.1 and iOS 8 do things differently.
Upvotes: 0
Views: 462
Reputation: 2854
--- Final answer in EDIT 2 ---
Well I did read about this before, and I did try it, but seems like I failed when I tried it earlier. I tried it again and it works now.
So what you want to do is set the canDisplayBannerAds
property of your view controller to YES
, then use the originalContentView
property instead of the normal view
one to set up your SKView
. Or if you want to access your view from inside a scene, that would be: self.view.window.rootViewController.originalContentView
. So basically any time you need to do something with your view that is specific to SKView
(like transition to a scene) and what cannot be done with a regular UIView
then use scene.view.window.rootViewController.originalContentView
instead of just scene.view
. The reason for this is that iAd somehow explicitly casts your view as UIView
when you set the canDisplayBannerAds
property in your view controller, so you cannot use the SKView
methods on your view anymore. The originalContentView
is a workaround for this.
This totally solved my problem, the app returns to its normal state now.
Nope, it didn't solve my problem. :(
Switching to another app and back while an ad is open now doesn't freeze the app. Good. But simply opening and closing an ad now freezes it the same way. This makes no sense...
So if I don't set canDisplayBannerAds
in my view controller, then leaving the app and coming back is the case when the app freezes. But just opening and closing an ad works fine. But if I do set canDisplayBannerAds
, then it's the other way around.
This really makes no sense. I'm very confused now...
Finally I solved it! But it wasn't easy god damn it...
So the problem was in the view hierarchy. If I add the ADBannerView
as a subview of my main view, then it freezes the app. This is strange, since that's the suggested method to add a banner to the screen.
If I don't add the banner as as subview manually, it sill appears on the screen though! I guess iAd is implemented in a way that once initialized, it will display itself on the screen no matter what. It appears a bit differently though. If it's not a subview of any view, then it comes in from the bottom with a "slide up" animation, resizing everything to a smaller box that's above it. This is a default behavior of an iAd banner I guess. So this way it doesn't freeze the screen, it works just fine! But since I wanted a bit more control over the banner (like animating it myself, instead of leaving that to the banner itself) I did the following:
In my view controller, I created an instance of SKView
(NOT by casting self.view
as SKView
as usual, but a completely new instance). Then I added that as a subview of self.view
. After that, I initialize the ADBannerView
, and add it as a subview of self.view
as well. Note that self.view
is not directly displaying anything, I use it as a "container" to hold the other two. The view hierarchy then looks something like this:
self.view
/ \
SKView ADBannerView
This way the ADBannerView
is not the direct subview or superview of my SKView (which is the main view my game uses) so it cannot screw it up because it doesn't have a concept of that view since they are both subviews of the same view. At this point I have no idea whether you need to set canDisplayBannerAds
in your view controller or not. I have it set, and it works. I didn't try leaving that out.
In a nutshell, I have full control of the banner view as well as my game view, and they can't mess with each other since they are not in a subview/superview relationship.
This was very tough to figure out, but here you go. If you also have problems with iAd and Sprite Kit, just organize your views like this.
Upvotes: 1