Loading a new SKScene into an existing SKView messes up the bounds - ios

I am trying to remove a SKScene and add a new one. The first presentScene: works fine, but the second one has messed up bounds, like this.
Here is my code.
#implementation GameViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[self loadLevel:1];
}
- (void)loadLevel:(int)level {
SKView * skView = (SKView *)self.view;
[skView presentScene:nil];
GameScene * gameScene;
switch (level) {
case 1:
gameScene = [Level1 sceneWithSize:skView.bounds.size];
break;
case 2:
gameScene = [Level2 sceneWithSize:skView.bounds.size];
break;
default:
break;
}
gameScene.scaleMode = SKSceneScaleModeAspectFill;
gameScene.gameViewController = self;
self.gameScene = gameScene;
[skView presentScene:gameScene transition:[SKTransition crossFadeWithDuration:1.0f]];
}

I didn't figure out why skView.bounds.size was changing between levels, but to correct it I hardcoded the size in. Let me know if there's a better way of doing it.
gameScene = [Level1 sceneWithSize:CGSizeMake(768, 1024)];

You run this in viewDidLoad, which means the view will be in portrait mode at that time:
- (void)viewDidLoad
{
[super viewDidLoad];
[self loadLevel:1];
}
To fix this, you should present the first scene in viewWillLayoutSubviews:
- (void)viewWillLayoutSubviews
{
[super viewWillLayoutSubviews];
if (((SKView*)self.view).scene == nil)
{
[self loadLevel:1];
}
}
It's important to add the nil check because viewWillLayoutSubviews will be called repeatedly, for instance when rotating the device.

Related

SpriteKit Game Not Filling Screen

I searched and I couldn't find my identical problem. My background image and nodes are all filling the screen properly, however on a 6+ the entire game leaves about .5 inch space on the top and bottom of the game. Even the status bar of the phone gets pushed down as well. On the 4s iPhone it works perfectly. Here is where I think the problem lays:
#implementation ViewController
- (void)viewWillLayoutSubviews
{
[super viewWillLayoutSubviews];
// Configure the view.
SKView * skView = (SKView *)self.view;
skView.showsFPS = YES;
skView.showsNodeCount = YES;
// Create and configure the scene.
SKScene * scene = [MyScene sceneWithSize:skView.bounds.size];
scene.scaleMode = SKSceneScaleModeAspectFill;
// Present the scene.
[skView presentScene:scene];
}
- (BOOL)shouldAutorotate
{
return YES;
}
- (NSUInteger)supportedInterfaceOrientations
{
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
return UIInterfaceOrientationMaskAllButUpsideDown;
} else {
return UIInterfaceOrientationMaskAll;
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}

SCNPhysicsContactDelegate must at least implement one selector

[SCNKit ERROR] SCNPhysicsContactDelegate must at least implement one selector
I am receiving the above error after setting my viewController as the contactDelegate for my project. I cannot find what I have forgotten to implement, below is my code for loading the scene.
- (void)viewDidLoad {
[super viewDidLoad];
SCNView *scnView = (SCNView *) self.view;
//set the background colour
scnView.backgroundColor = [UIColor blackColor];
//setup the scene
SCNScene *scene = [self setupScene];
//present it
scnView.scene = scene;
//initial point of view
scnView.pointOfView = _cameraNode;
//plug game logic
scnView.delegate = self;
//physicsworld
scnView.scene.physicsWorld.gravity = SCNVector3Make(0, 0, 0);
scnView.scene.physicsWorld.contactDelegate = self;
[super viewDidLoad];
}
- (void)didBeginContact:(SCNPhysicsContact *)contact {
NSLog(#"contact");
}
.h
#interface GameViewController : UIViewController <SCNSceneRendererDelegate, SCNPhysicsContactDelegate>
#end
You're not implementing any methods from the protocol, so it's an error to declare conformance.
You probably want to implement physicsWorld:didBeginContact:, not just didBeginContact:.

iOS: Change view controller to landscape view

I am using storyboard and all the view controllers have navigation controller. The app is at portrait and landscape mode.
One of the view controllers must always in landscape when I tapped a button.
After researched all the questions in stackoverflow, I still cannot find any solutions.
I am using sprite so if auto-rotate will messed up my sprite nodes.
- (void)viewDidLoad
{
[super viewDidLoad];
// Configure the view.
SKView * skView = (SKView *)self.view;
skView.showsFPS = YES;
skView.showsNodeCount = YES;
// Create and configure the scene.
SKScene * scene = [MyScene sceneWithSize:skView.bounds.size];
scene.scaleMode = SKSceneScaleModeAspectFill;
// Present the scene.
[skView presentScene:scene];
}
-(BOOL)shouldAutorotate
{
return NO;
}
-(NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskLandscape;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return UIInterfaceOrientationLandscapeRight;
}

Adding iAds to Sprite Kit Landscape

I have the following code in my View Controller:
- (void)viewWillLayoutSubviews {
[super viewWillLayoutSubviews];
// Configure the view.
SKView * skView = (SKView *)self.view;
if (!skView.scene) {
skView.showsFPS = NO;
skView.showsNodeCount = NO;
skView.showsDrawCount = NO;
// Create and configure the scene.
SKScene * scene = [MenuScene sceneWithSize:skView.bounds.size];
scene.scaleMode = SKSceneScaleModeAspectFill;
self.canDisplayBannerAds = YES;
// Present the scene.
[skView presentScene:scene];
}
}
When I run the application it crashes immediately. I get the following error message:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UIView scene]: unrecognized selector sent to instance
I can't use SKView *skView = (SKView *)self.originalContentView; because the app is in landscape mode. Is there a way to display iAds in a Landscap Sprite Kit game?
EDIT:
I just added this code to the view controller, but I get the same results..
#pragma mark iAd Delegate Methods
-(void)bannerViewDidLoadAd:(ADBannerView *)banner {
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:1];
[banner setAlpha:1];
[UIView commitAnimations];
}
I ran into this issue and solved it via creating an ABBannerView property and adding that as a subView.
In my ViewController class :
adView = [[ADBannerView alloc] initWithFrame:CGRectZero];
adView.delegate = self;
[adView setFrame:CGRectMake(0, 0, 1024, 768)]; // set to your screen dimensions
[self.view addSubview:adView];
Important to NOT set the canDisplayBannerAds property of your view controller.
I believe what is happening is that if you do set the canDisplayBannerAds property to true, the view is modified and is no longer compatible with a SKView, and no longer has a scene property.
I did have to set the frame, so that the dimensions were correct otherwise it was portrait.
You can solve this without having to manually configure the ad banner. You do still have to use the original content view, but making it work correctly is just a matter of putting everything together in the right stages of the view controller's life cycle. All you have to do is enable canDisplayBannerAds: in awakeFromNib. Then set the SKView you create to either the view controller's view or originalContentView depending on the existence of the original content view.
- (void)awakeFromNib
{
[super awakeFromNib];
[self setCanDisplayBannerAds:YES];
}
- (void)viewWillLayoutSubviews
{
[super viewWillLayoutSubviews];
SKView *skView = nil;
if (self.originalContentView) {
skView = (SKView *)self.originalContentView;
}else{
skView = (SKView *)self.view;
}
[skView setShowsDrawCount:YES];
[skView setShowsFPS:YES];
[skView setShowsNodeCount:YES];
SKScene *scene = [MyScene sceneWithSize:skView.bounds.size];
[scene setScaleMode:SKSceneScaleModeFill];
[skView presentScene:scene];
}
None of the above answers works for me. Below code is tested in iOS 7 and 8 and works just fine.
Add below lines in header file
#import <iAd/iAd.h>
#interface GameViewController : UIViewController<ADBannerViewDelegate>{
//iAd
ADBannerView *adView;
}
In Implementation file .m add below code
#import "GameViewController.h"
#import "GameScene.h"
#implementation GameViewController
-(void)viewWillLayoutSubviews{
[super viewWillLayoutSubviews];
SKView *skView;
if (self.originalContentView) {
skView = (SKView *)self.originalContentView;
}
if (!skView.scene) {
//[skView setShowsDrawCount:YES];
//[skView setShowsFPS:YES];
//[skView setShowsNodeCount:YES];
//Improve Performance
skView.ignoresSiblingOrder = YES;
GameScene *scene = [GameScene sceneWithSize:skView.bounds.size];
[scene setScaleMode:SKSceneScaleModeFill];
[skView presentScene:scene];
}
}
- (void)awakeFromNib{
[super awakeFromNib];
CGRect screenRect = [[UIScreen mainScreen] bounds];
adView = [[ADBannerView alloc] initWithFrame:CGRectZero];
adView.frame = CGRectMake(0, 0, screenRect.size.width, adView.frame.size.height);
adView.delegate=self;
[self.view addSubview:adView];
}
//iAd
-(void)bannerViewDidLoadAd:(ADBannerView *)banner {
[adView setAlpha:1.0];
NSLog(#"Show Ad");
}
-(void)bannerView:(ADBannerView *)banner didFailToReceiveAdWithError:(NSError *)error {
[adView setAlpha:0];
NSLog(#"Hide Ad");
}
//
- (BOOL)shouldAutorotate
{
return YES;
}
- (NSUInteger)supportedInterfaceOrientations
{
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
return UIInterfaceOrientationMaskAllButUpsideDown;
} else {
return UIInterfaceOrientationMaskAll;
}
}
-(BOOL)prefersStatusBarHidden{
return YES;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#end

Hide iAd in Sprite Kit Scene

I have added iAds to my Sprite Kit game with the following code:
In the viewController.h file
#property (strong, nonatomic) IBOutlet ADBannerView * adBannerView;
In the viewController.m file
- (void)viewWillLayoutSubviews
{
[super viewWillLayoutSubviews];
// Configure the view.
SKView * skView = (SKView *)self.view;
if (!skView.scene) {
// Create and configure the scene.
SKScene * scene = [MenuScene sceneWithSize:skView.bounds.size];
scene.scaleMode = SKSceneScaleModeAspectFill;
_adBannerView = [[ADBannerView alloc] initWithFrame:CGRectZero];
_adBannerView.delegate = self;
[_adBannerView setFrame:CGRectMake(0, 0, 460, 320)];
[self.view addSubview:_adBannerView];
// Present the scene.
[skView presentScene:scene];
}
}
This shows the iAd in every scene. Is there a way to hide the iAd in some of the scenes?
Apple's iAd Programming Guide says:
Only create a banner view when you intend to display it to the user. Otherwise, it may cycle through ads and deplete the list of available advertising for your application.
Is this at all possible with scenes?
yes there is a way to hide the iAd in some of the scenes.
- (void)viewDidLoad
{
[super viewDidLoad];
//Add view controller as observer
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(handleNotification:) name:#"hideAd" object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(handleNotification:) name:#"showAd" object:nil];
// Configure the view.
SKView * skView = (SKView *)self.view;
skView.showsFPS = NO;
skView.showsNodeCount = NO;
// Create and configure the scene.
SKScene * scene = [MyScene sceneWithSize:skView.bounds.size];
scene.scaleMode = SKSceneScaleModeAspectFill;
// Present the scene.
[skView presentScene:scene];
self.canDisplayBannerAds = YES;
adView = [[ADBannerView alloc] initWithFrame:CGRectZero];
adView.frame = CGRectOffset(adView.frame, 0, 0.0f);
adView.delegate=self;
[self.view addSubview:adView];
self.bannerIsVisible=NO;
}
//Handle Notification
- (void)handleNotification:(NSNotification *)notification
{
if ([notification.name isEqualToString:#"hideAd"]) {
[self hidesBanner];
} else if ([notification.name isEqualToString:#"showAd"]) {
[self showBanner];
}
}
And in your scene in which you want to hide banner...
[[NSNotificationCenter defaultCenter] postNotificationName:#"showAd" object:nil];
//Sends message to viewcontroller to show ad.
[[NSNotificationCenter defaultCenter] postNotificationName:#"hideAd" object:nil];
//Sends message to viewcontroller to hide ad.
Well, In your specific scene follow the Apple's guide(same place as your question) on this issue at the link below, look at the section that says "banner view best practices":
https://developer.apple.com/library/ios/documentation/userexperience/conceptual/iAd_Guide/WorkingwithBannerViews/WorkingwithBannerViews.html#//apple_ref/doc/uid/TP40009881-CH4-SW3
In Summary they say: "remove the banner view from the view hierarchy, set its delegate to nil"
The most clean solution is to declare and implement a protocol to let the UIViewController know from the scene that it should hide the ad.
#protocol MySceneDelegate <NSObject>
- (void)hideAd;
#end
#interface MyScene : SKScene
#property (weak) id <MySceneDelegate> delegate;
#end
View controller that shows the scene should implement a hideAd method and set itself as a delegate of the scene. Example:
- (void)viewDidLoad
{
[super viewDidLoad];
// Configure the view.
SKView * skView = (SKView *)self.view;
skView.showsFPS = YES;
skView.showsNodeCount = YES;
// Create and configure the scene.
MyScene * scene = [MyScene sceneWithSize:skView.bounds.size];
scene.scaleMode = SKSceneScaleModeAspectFill;
// Set the delegate
[scene setDelegate:self];
// Present the scene.
[skView presentScene:scene];
}
Then in the scene you can call the hideAd method of the view controller which was set as a delegate:
if ([_delegate respondsToSelector:#selector(closeScene)])
{
[_delegate performSelector:#selector(hideAd)];
}
And remove the banner in hideAd method.
To hide the banner view, you should:
Resize your banner view's frame to be offscreen Resize your content
view's frame to cover the space originally hosting the banner
Hope it helps.

Resources