3
votes

I have a project where I use storyboards, a navigation controller, with storyboard segues. I have a cocos2d view in one of the views. The view GameLayer has the class CCLayer which is embeded in cocos2dViewController cocos2dViewController : CCViewController and CCViewController is again the subclass of CCViewController : UIViewController.So, in my Cocos2d view, which is a CCLayer, can't use the [self performSegue....] method, as it isn't a direct subclass of uiviewcontroller or something.

I tried

    GameOverViewController *gameOver = [[GameOverViewController alloc]init];
    UIViewController* rootViewController = [UIApplication sharedApplication].keyWindow.rootViewController;
    [rootViewController presentViewController:gameOver animated:YES completion:NULL];

But that just got me Warning: Attempt to present <GameOverViewController: 0x107d0060> on <UINavigationController: 0x9d33890> whose view is not in the window hierarchy!

I also tried

    GameOverViewController *gameOver = [[GameOverViewController alloc]init];
    AppController *app = (AppController*) [[UIApplication sharedApplication] delegate];
    [[app navController] presentModalViewController:gameOver animated:YES];

but that didn't do anything at all

So I either need to perform a selector on one of the higher-up classes which is a subclass of UIViewController, in which case, how do I do that?

Or I need to perform the segue from my CCLayer, in which case how do I do that? Either is fine with me :)

Edit: Clarified question: I need to do a segue from my GameLayer which is a CCLayersubclass, and because of this it can't use the normal segue methods. GameLayer is embedded or whatnot in cocos2dViewControllerwhich is a subclass of CCViewController that finally is a subclass of UIViewController. I use storyboarding in my project, and CCViewController is the view controller that responds to IBOutlets. So I need to push the segue from this file somehow from my Game Layer. You can see the game system here: mediafire.com/?7dil7uolo5k1syr from a template I made. I want to pull a segue out from the game view controller to another view. So far i've tried various methods of doing it in another file, ccdirector and other.

For those not familiar with cocos2d, CCLayer is a subclass of CCNode, which is a subclass of NSObject. So you can see this as a question on how to do a segue in a UIViewController from a NSObject.

So tl;dr: Do a storyboard segue from CCLayer(NSObject) that is embedded in a subclass of a subclass of UIViewController.

5

5 Answers

1
votes

If you don't want to use notifications here's how you can do it:

I changed the code and made this to work without Notifications. The problem was to get a reference to the cocos2dViewController ViewController so that you can perform the segue from there.

You first need to get a reference to the rootViewController which is a UINavigationController then you just get the last controller from the navigation/root view controller:

[navC.viewControllers lastObject];

This is the code that you run when the next button is touched:

UIViewController *root = [[[UIApplication sharedApplication] delegate] window].rootViewController;
if ( [root isKindOfClass:[UINavigationController class]] ) {
    UINavigationController *navC = (UINavigationController *)root;
    cocos2dViewController *cocosVC = (cocos2dViewController *)[navC.viewControllers lastObject];
    [cocosVC performSelector:@selector(asdf)];
}

Then in the cocos2dViewController you have the asdf selector that simply performs the segue:

- (void)asdf
{
    [self performSegueWithIdentifier:@"segueFromCocos" sender:self];
}
1
votes

Ok, I think I got this to work. I tried many approaches and the main problem is that the GameLayer is a CCLayer and it's difficult to find the ViewController that includes it.

You need a reference to the cocos2dViewController ViewController to perform the segue and my solution is not ideal but it works in this case.

I used the NSNotificationCenter to post a notification from the GameLayer and then added the cocos2dViewController controller as an observer. When the cocos2dViewController controller receives the notification you perform the segue

[self performSegueWithIdentifier:@"segueFromCocos" sender:self];

These are the exact steps:

  1. Drag a new View Controller to the Storyboard
  2. Create a push segue from the cocos2dViewController to the newly created ViewController and add this segue Identifier: "segueFromCocos" to the segue
  3. change the segue that segues to the cocos2dViewController from modal to push
  4. add a button to the GameLayer in the init method:

    UIButton *nextButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    [nextButton setTitle:@"next" forState:UIControlStateNormal];
    nextButton.tag = 123;
    nextButton.frame = CGRectMake(10.0, 10.0, 90.0, 40.0);
    [nextButton addTarget:self
                   action:@selector(nextButtonClicked:)
         forControlEvents:UIControlEventTouchDown];
    
    UIView *glView = [CCDirector sharedDirector].view;
    [glView addSubview:nextButton];
    

    this button will push the notification in the nextButtonClicked: selector

    • (void)nextButtonClicked:(id)sender { NSLog(@"notification posted"); [[NSNotificationCenter defaultCenter] postNotificationName:@"TestNotification" object:self]; }
  5. Then in the viewDidLoad method of the cocos2dViewController view controller register cocos2dViewController as an observer:

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(receiveTestNotification:) name:@"TestNotification" object:nil];

  6. And then finally in the receiveTestNotification: perform the segue:

    • (void)receiveTestNotification:(NSNotification *) notification { if ([[notification name] isEqualToString:@"TestNotification"]) { NSLog (@"Successfully received the test notification! %@", [self class]);

      [self performSegueWithIdentifier:@"segueFromCocos" sender:self];
      

      } }

Sorry for the code formatting issues btw :)

UPDATE - No NotificationCenter

I changed the code and made this to work without Notifications. The problem was to get a reference to the cocos2dViewController ViewController so that you can perform the segue from there.

You first need to get a reference to the rootViewController which is a UINavigationController then you just get the last controller from the navigation/root view controller:

[navC.viewControllers lastObject];

This is the code that you run when the next button is touched:

UIViewController *root = [[[UIApplication sharedApplication] delegate] window].rootViewController;
if ( [root isKindOfClass:[UINavigationController class]] ) {
    UINavigationController *navC = (UINavigationController *)root;
    cocos2dViewController *cocosVC = (cocos2dViewController *)[navC.viewControllers lastObject];
    [cocosVC performSelector:@selector(asdf)];
}

Then in the cocos2dViewController you have the asdf selector that simply performs the segue:

- (void)asdf
{
    [self performSegueWithIdentifier:@"segueFromCocos" sender:self];
}
0
votes

I wouldn't recommend messing with segues when working with Cocos2d. Instead, you should show your gameover screen as a whole new CCScene, or as a CCLayer, just adding it as a child of your parent CCNode. If your gameover screen is some kind of popup/dialog, you can also try CCLayerColor with a transparent black background color.

0
votes
GameOverViewController *gameOver = [self.storyboard instantiateViewControllerWithIdentifier:@"GameOverViewController"];
UINavigationController  *navController=[[UINavigationController new]initWithRootViewController:gameOver];
[self.navigationController presentViewController:navController animated:YES completion:Nil];

You should try below link which is shows the same error --Warning: Attempt to present : 0xab5d9d0 on whose view is not in the window hierarchy! Whose view is not in window hierarchy issue

0
votes

I agree wholeheartedly with ssantos and their solution, but if you insist on trying to mash together UIKit and cocos2D:

Instead of "[self performSegue....] method", try [[CCDirector sharedDirector] performSegue....] since the director is a subclass of UIViewController.

I can't say for certain if this will work since you are using cocos2D in an unconventional way.


Another observation:

You say your GameLayer is embedded in a cocos2dViewController.. does it have a reference to that view controller? Could you just call performSegue on that?