james james - 5 months ago 149
Swift Question

(Swift2 SpriteKit) How to transition between an SKScene and a UIViewController efficiently?

There seems to be so many contradicting ideas on this topic.
I simply wish to have my menu in a UIViewController and my game in the SKScene

In my SKScene I used:

self.removeFromParent()
self.view?.presentScene(nil)


The nodes are removed but the scene is still in place as I still have the grey background and fps counter. Can I return to the View aspect of the UIViewController and hide the scene?

My method one implementation:

RootViewController:

class RootViewController: UIViewController {

var menu = MenuViewController()
var game = GameViewController()

override func viewDidLoad() {
super.viewDidLoad()

print("root")
MenuPresent()
}

func GamePresent() {
self.addChildViewController(game)
self.view.addSubview((game.view)!)
game.didMoveToParentViewController(self)
}

func MenuPresent() {
self.addChildViewController(menu)
self.view.addSubview((menu.view)!)
menu.didMoveToParentViewController(self)
}

func menuDismiss() {
menu.willMoveToParentViewController(nil)
menu.removeFromParentViewController()
menu.view.removeFromSuperview()
}


}

MenuViewController:

class MenuViewController: UIViewController {

//var root = RootViewController()

override func viewDidLoad() {
super.viewDidLoad()

print("menu")
}
}


The print("menu") appears in my console, But the actual view and all assets of the MenuViewController do no appear.

On the other hand my GameViewController and it's SKScene work fine.

Answer

First you need to know that SKScene and UIViewController are totally two different things. The hierarchy is typically as following:

UIViewController --> UIView(SKView) --> SKScene

So you SKScene is presented in a SKView which can be a UIView, then the UIView is presented in a UIViewController.

Once you know the hierarchy, everything is easy. There are many ways to use different UIViewController for menu and GameScene stuff.

Method One

For example, you can have a RootViewController, a GameViewController and a MenuViewController. The RootViewController is the initial ViewController when the app launches.

In the RootViewController, you can create a function to present the GameViewController:

  func setupGameViewController() {
    self.gameViewController = GameViewController()
    self.addChildViewController(gameViewController!)
    self.view.addSubview((gameViewController!.view)!)
    gameViewController?.didMoveToParentViewController(self)
  }

You need to present you SKScene in GameViewController, I guess you should be familiar with this step.

Then when you need to display the menu, you can add the MenuViewController to RootViewController with a function like:

  func setupMenuViewController() {
    self.menuViewController = MenuViewController()
    self.addChildViewController(menuViewController!)
    self.view.addSubview((menuViewController!.view)!)
    menuViewController?.didMoveToParentViewController(self)
  }

You also need to present you menuView in this ViewController, which I suppose you already know.

Also create a function to dismiss the MenuViewController:

  func removeMenuViewController(){
    self.menuViewController?.willMoveToParentViewController(nil)
    self.menuViewController?.removeFromParentViewController()
    self.menuViewController?.view.removeFromSuperview()
  }

And everything is done.

Method Two

You can also have only one UIViewController, but create you menu as a UIView, then you can use self.view.addSuview(menuView) to present your menu. Of course you root view, which is the self.view as SKView is still there, but it doesn't matter because it's hidden behind the menuView.

A Note for your updated question

You cannot remove the scene because the scene is actually self.view as SKView, self.view is the root view of a UIViewController, it cannot be removed. If you really want to remove you scene (for most cases, it's not necessary), you can create a new SKView that present your SKScene, then add this SKView to your UIViewController by self.view.addSubview(skView), when you want to completely remove the scene, just use skView.removeFromSuperview().

Comments