Presenting UIViewController from SKScene

Solution 1:

Don't try to present UIViewController from SKScene directly, this breaks MVC pattern. SKScene is part of View, View should not know anything about ViewController.

Instead, you can use NSNotificationCenter to notify SKScene's UIViewController that it should present another UIViewController:

In SKScene's UIViewController:

- (void)awakeFromNib {
    [[NSNotificationCenter defaultCenter] 
        addObserver:self
        selector:@selector(goToGameOverViewController:)
        name:@"GoToGameOverViewController"
        object:nil];
}

.

-(void)goToGameOverViewController:(NSNotification *) notification {
    // Perform a segue or present ViewController directly
    //[self performSegueWithIdentifier:@"GameOverSegue" sender:self];
    HelpViewController *helpVC = [[HelpViewController alloc]initWithNibName:@"HelpViewController" bundle:nil];
    [self presentViewController:helpVC animated: YES completion:nil];
}

.

- (void) dealloc
{
    // If you don't remove yourself as an observer, the Notification Center
    // will continue to try and send notification objects to the deallocated
    // object.
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

In SKScene:

- (void)gameOver {
    [[NSNotificationCenter defaultCenter]
         postNotificationName:@"GoToGameOverViewController" object:self];
}

Solution 2:

Your problem is twofold:

  1. Casting a UIViewController to SKScene.
    SKScene *sks = (SKScene*)helpVC;
    Casting should be done in other scenarios : e.g. between primitives, between UI Foundation and CF classes or between a subclass and it's super (but shouldn't be done in the opposite way!).
    Long story short - not in this scenario (these classes have nothing in common)

  2. You ask your view to present a scene, but pass a viewController instead :
    [self.view presentScene:sks];
    So, what happened here ?
    presentScene: thinks they got an SKScene object, thus sends some message to sks. A message that sks does not understand... and crashes.

How to solve this ?

Grab your presenting view controller.
Either in a global, or just grab your root view controller from the app delegate:

UIViewController *rootVC = [UIApplication sharedApplication].keyWindow.rootViewController;
[rootVC presentViewController:yourPresentedVC animated:YES completion:nil];

In case you're using a tab bar/navigation controller you should grab their presented VC.