I am trying to display an admob interstitial ad after receiving a successful ad call :
- (void)interstitialDidReceiveAd:(GADInterstitial *)ad {
DLog(#"interstitialDidReceiveAd");
[ad presentFromRootViewController:self];
}
self is the ViewController instance (which is the root view controller as implemented by the SpriteKit template) and for some reason it displays the ad (which is good) but restarts the entire scene.
I should note that the ad request is done way after the scene is created and being run.
I also noticed this behavior when I tried adding a view to this controller's view property (which is a SpriteKit SKView instance)
And ideas ?
As I thought... I was doing something wrong...
If you stumble upon this problem :
I create my scene in the view controller's method viewWillLayoutSubviews (until this method the view bounds are NOT updated with the desired orientation)
When the interstitial ad is presented this method is called again and of course the scene is recreated as well. So just add a boolean member indicating if the scene was already loaded for creating the scene only once.
Poof, problem is gone:)
There's something funky going on here that's more than just displaying an interstitial. I have a sprite kit project setup where I display the UIImagePickerController from the rootViewController via:
#pragma mark - ImageCatureDelegate methods
-(void)requestImagePicker
{
UIImagePickerController *imagePicker = [UIImagePickerController new];
imagePicker.delegate = self;
[self presentViewController:imagePicker animated:YES completion:nil];
}
#pragma mark - UIImagePickerControllerDelegate
-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
UIImage *image = [info objectForKey:#"UIImagePickerControllerOriginalImage"];
[picker dismissViewControllerAnimated:YES completion:^{
SKTexture *imageTexture = [SKTexture textureWithImage:image];
CIFilter *sepia = [CIFilter filterWithName:#"CISepiaTone"];
[sepia setValue:#(0.8) forKey:kCIInputIntensityKey];
imageTexture = [imageTexture textureByApplyingCIFilter:sepia];
SKView *view = (SKView *)self.view;
MyScene *currentScene = (MyScene *)view.scene;
[currentScene setPhotoTexture:imageTexture];
}];
}
during the callback, nothing special is done except dismissing the modal. If admob is pushing a new VC you may see this issue, but if it's displaying a literal modal VC modally... then you shouldn't see this issue. You may want to look into exactly what happens when you push an interstitial ad. If this is the case, and this is the only method you can use, You'll have to NSCode or something to keep the game's state so it can be loaded back up when the interstitial disappears.
Edit:
To append to your solution above, if you use viewWillLayoutSubviews to correct the portrait/landscape issue in SpriteKit, be sure to do it as such so this only happens 'once'
- (void)viewWillLayoutSubviews
{
[super viewWillLayoutSubviews];
// Configure the view.
SKView * skView = (SKView *)self.view;
if (!skView.scene) {
skView.showsFPS = YES;
skView.showsNodeCount = YES;
// Create and configure the scene.
MyScene * scene = [[MyScene alloc] initWithSize:skView.bounds.size andLevelNumber:1];
scene.scaleMode = SKSceneScaleModeAspectFill;
// Present the scene.
[skView presentScene:scene];
}
}
Related
I am not an experienced app developer, I am modifying an obj c spriteKit game. The concept that I need to implement is, when the user clicks a button in the menu scene (SKScene) "talk" to the navigation controller and show another storyboard who they gave me.
For this I use a delegate from the scene, in order to "talk" to the gameview controller's navigation controller and reference the other storyboard.
Everything works, but when from the starting scene(EndGameScene in the code) the user goes to the gamescene and back then it does not work. And there is no error message!
GameViewController.m
SKView * skView = (SKView *)self.view;
skView.ignoresSiblingOrder = YES;
_scene = [[EndGameScene alloc] initWithSize:self.view.frame.size];
_scene.scaleMode = SKSceneScaleModeAspectFill;
self.scene.delegate = self;
// Present the scene.
[skView presentScene:_scene];
...
//delegation method
-(void)showDifferentView {
UIStoryboard *storyboard = [SettingsViewController settingsStoryBoard];
SettingsViewController *controller = [storyboard instantiateViewControllerWithIdentifier:SettingsControllerStoryboardIdentifier];
[self.navigationController pushViewController:controller animated:YES];
self.navigationController.navigationBarHidden = NO;
}
GameScene.m
-(void)backToHomePage{
[self removeAllChildren];
[self removeAllActions];
[self removeFromParent];
SKScene *endGameScene = [[EndGameScene alloc] initWithSize:self.size];
endGameScene.scaleMode = SKSceneScaleModeAspectFill;
GameViewController *gameVC = [[GameViewController alloc] init];
endGameScene.delegate = gameVC; //when add that line crashes
SKTransition *reveal = [SKTransition fadeWithDuration:0];
[self.view presentScene:endGameScene transition:reveal];
}
If I don't go to the GameScene there is no problem. The problem is when the user goes to the GameScene and comes back.
I would appreciate any help
Thanks
I enabled Game Center functionality in my SpriteKit game in the ViewController. Everything works fine, but I want to show the Leaderboard in another Scene after a Button is touched. I imported everything correctly. My project crashes now after touching the 'HighScoreButton' in the Head.m file, with following Output:
'NSInvalidArgumentException', reason: '-[UIView presentScene:]: unrecognized selector sent to instance 0x7feec2ff2920'
My recent Code that doesn't work:
ViewController.h
#interface ViewController : UIViewController <GKGameCenterControllerDelegate>
- (void)showLeaderboardAndAchievements:(BOOL)shouldShowLeaderboard;
+ (ViewController*)defaultHelper;
#end
ViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
// Configure the view.
SKView * skView = (SKView *)self.view;
[self authenticateLocalPlayer];
// Create and configure the scene.
SKScene * scene = [Head sceneWithSize:skView.bounds.size];
scene.scaleMode = SKSceneScaleModeAspectFill;
self.canDisplayBannerAds = YES;
// Present the scene.
[skView presentScene:scene];
}
static ViewController *_sharedHelper = nil;
+ (ViewController*)defaultHelper {
// dispatch_once will ensure that the method is only called once (thread-safe)
static dispatch_once_t pred = 0;
dispatch_once(&pred, ^{
_sharedHelper = [[ViewController alloc] init];
});
return _sharedHelper;
}
-(void)showLeaderboardAndAchievements:(BOOL)shouldShowLeaderboard{
GKGameCenterViewController *gcViewController = [[GKGameCenterViewController alloc] init];
gcViewController.gameCenterDelegate = self;
if (shouldShowLeaderboard) {
gcViewController.viewState = GKGameCenterViewControllerStateLeaderboards;
gcViewController.leaderboardIdentifier = _leaderboardIdentifier;
}
else{
gcViewController.viewState = GKGameCenterViewControllerStateAchievements;
}
[self presentViewController:gcViewController animated:YES completion:nil];
}
-(void)gameCenterViewControllerDidFinish:(GKGameCenterViewController *)gameCenterViewController
{
[gameCenterViewController dismissViewControllerAnimated:YES completion:nil];
}
Head.m
if ([Node.name isEqualToString:#"HighScoreButton"]){
[[ViewController defaultHelper] showLeaderboardAndAchievements:YES];
}
I believe the problem is with the singleton initialization of your view controller (which is correct btw).
When first loaded the view controller is created "behind the scenes" (I am not sure where exactly but I'll look for it) and its view is initialized with an skView instance. (And since it is not created by you your singleton instance is not initialized and a different ViewController instance is used)
Since your defaultHelper method is not used on the first creation of your view controller you are creating a new view controller which its view is an instance of UIView and it does not comply to the SKView methods (such as setShowFPS and presentScene). And of course since you create a new view controller it is not part of the view hierarchy.
What I recommend for you is to retrieve the view controller as follows :
if ([Node.name isEqualToString:#"HighScoreButton"]){
ViewController *viewController = self.view.window.rootViewController;
[viewController showLeaderboardAndAchievements:YES];
}
My situation is that I have a GameMenuScene and after the user chooses a level, I want to present the LevelScene. But I do not want to have the previous GameMenuScene discarded because the LevelScene is actually a #property of GameMenuScene and whether or not the user completes the level is to be saved as a #property of LevelScene, which the GameMenuScene should be able to access after the user finishes or exits the level. If I simply use presentScene:transition, the GameMenuScene is discarded and the information cannot pass back.
My question: Is there a way to stack or push the scenes on top of each other without discarding the previous (preferably using a transition animation)? I could not find a method for this specific purpose in the Apple Documentation for SKScene.
Note: Another StackOverflow answer to a similar question suggests to create a new UIViewController, present the LevelScene in there and then present the UIViewController modally. But I was hoping there was an existing method in the Apple Documentation that I have missed to present the scene itself scene modally without having to create UIVIewControllers.
There is no navigation controller-like capability for SKScenes that allows you to push and pop scenes. You will need to write code to manage and present your scenes.
Here's a simple view controller implementation that allows you to switch between two scenes (by swiping) without discarding the other scene.
#interface ViewController()
#property BOOL viewFlag;
#property SKScene *scene1;
#property SKScene *scene2;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Configure the view.
SKView * skView = (SKView *)self.view;
skView.showsFPS = YES;
skView.showsNodeCount = YES;
UISwipeGestureRecognizer *swipeGesture = [[UISwipeGestureRecognizer alloc]
initWithTarget:self action:#selector(handleSwipeGesture:)];
swipeGesture.direction = UISwipeGestureRecognizerDirectionRight;
[self.view addGestureRecognizer:swipeGesture];
// Create and configure scene 1.
SKScene * scene = [MyScene sceneWithSize:skView.bounds.size];
scene.scaleMode = SKSceneScaleModeAspectFill;
self.scene1 = scene;
// Present the scene 1.
[skView presentScene:scene];
// Create and configure scene 2.
scene = [MySecondScene sceneWithSize:skView.bounds.size];
scene.scaleMode = SKSceneScaleModeAspectFill;
self.scene2 = scene;
}
- (void) handleSwipeGesture:(id)sender
{
SKView * skView = (SKView *)self.view;
_viewFlag = !_viewFlag;
if (_viewFlag) {
[skView presentScene:_scene1];
}
else {
[skView presentScene:_scene2];
}
}
#end
I get the following error when I try to ad iAds to my SpriteKit game. Anyone know why this is?
-[UIView scene]: unrecognized selector sent to instance 0x10da31fb0
ViewController.m
- (void)viewWillLayoutSubviews
{
[super viewWillLayoutSubviews];
// Configure the view.
SKView * skView = (SKView *)self.view;
if (!skView.scene) {
skView.showsFPS = NO;
skView.showsNodeCount = NO;
// Create and configure the scene.
SKScene * scene = [HomeScreen sceneWithSize:skView.bounds.size];
scene.scaleMode = SKSceneScaleModeAspectFill;
// Present the scene.
[skView presentScene:scene];
self.canDisplayBannerAds = YES;
}
}
In addition to my comment (regarding iAD) the error shows me that somehow to view's class is not correct. It should be SKView.
To correct this go to Interface Builder and for that particular view change the class using the Identity inspector (3rd "tab") to SKView.
It happened to me when manually created from the scratch a SpriteKit project.
Hope it helps! :)
I have a ViewController in which i present my SKScene. So before i present the SKScene i use the UIImagePickerController in the ViewController to take a picture and in the
-imagePickerController:didFinishPickingMediaWithInfo:
when the UIImagePickerController dismiss i start to present the scene and initialize it :
[self dismissViewControllerAnimated:NO completion:nil];
SKView * skView = (SKView *)self.view;
skView.showsFPS = NO;
skView.showsNodeCount = NO;
// Create and configure the scene.
MultiplayerScene * scene = [[MultiplayerScene alloc]initWithSize:self.view.frame.size andPicture:self.imageView];
scene.scaleMode = SKSceneScaleModeAspectFill;
// Present the scene.
// --This works also just on the Simulator and not on Physical Device
[scene setBackgroundColor:[UIColor redColor]];
// --
[skView presentScene:scene];
So, what happened in the Simulator it seems to work my Image is showing on the Screen,, but on physical device the picker dismiss and i see just the gray Background.
I have tried 2 Days to fix this but i actually i think that the problem is that the SKScene isn't first responder or will not initialize correct, but when i debug on the physical device the picture comes correct.
The Same Problem will be produce with any type of Picker / Browser.
On the GrayBackground (the default color for SKScene) the touch-events are working
What can i do here to handle it right?
Best
Konstantin
SOLVED
Today i update my Xcode to 5.1 and then everything works fine (head meets table)... Thanks for Mc.Lover for his clear answer!!
Do something like this :
- (void)myMethod {
MultiplayerScene * scene = [[MultiplayerScene alloc]initWithSize:self.view.frame.size andPicture:self.imageView];
scene.scaleMode = SKSceneScaleModeAspectFill;
[skView presentScene:scene];
}
[self dismissViewControllerAnimated:NO completion: ^ {[self myMethod]; }];
hope this helps !