Search code examples
iosswiftuiviewcontrollersprite-kitskscene

How to do a reference of my UIViewController in the SKScene?


I need to have my UIViewController as a property of my GameScene. So before calling presentScene, I set the property with the UIViewController.

My UIViewController class:

class GameViewController: UIViewController {

    var scene:GameScene!

    override func viewDidLoad() {
        super.viewDidLoad()

        if let scene = GKScene(fileNamed: "GameScene") {

            // Get the SKScene from the loaded GKScene
            if let sceneNode = scene.rootNode as! GameScene? {

                self.scene=sceneNode

                // Set the scale mode to scale to fit the window
                sceneNode.scaleMode = .aspectFill

                // Present the scene
                if let view = self.view as! SKView? {

                    sceneNode.gameViewController = self //SET THE PROPERTY
                    view.presentScene(sceneNode)

                    view.ignoresSiblingOrder = true
                    view.showsPhysics = true
                    view.showsFPS = true
                   view.showsNodeCount = true    
                }
            }
        }
    }
}

My GameScene class:

class GameScene: SKScene, SKPhysicsContactDelegate {

    // MARK: Properties
    var gameViewController:GameViewController!

    override func sceneDidLoad() {

         print(self.gameViewController) // -> CRASH
         //DO SOME STUFF
    }
}

I get this crash:

fatal error: unexpectedly found nil while unwrapping an Optional value

EDIT 1

So I tried to use:

override func didMove(to view: SKView) {
    super.didMove(to: view)

    self.physicsWorld.contactDelegate = self
    self.physicsBody!.categoryBitMask = ColliderType.DeviceBounds.rawValue

    //ALL MY INIT CODE
}


override func sceneDidLoad() {
    super.sceneDidLoad()
    //NOTHING HERE
}

But the physic world doesn't work anymore. My crash disappears, but the physic world is broken.

In the console I have:

--->GameViewController - viewDidLoad()<---
--->GameScene - sceneDidLoad()<---
--->GameScene - sceneDidLoad()<---
--->GameScene - didMove(to:)<---
--->GameViewController - viewWillAppear<---
--->GameViewController - viewDidAppear<---

Solution

  • The sceneDidLoad function gets called before you have set the reference to gameViewController. You should use the SKScene didMoveToView function instead.

    override func didMove(to view: SKView) {
        print(self.gameViewController)
    }
    

    You could set breakpoints in Xcode (in your gameScene and in gameViewController) and see the timing of events. As an aside, it is a better practice to implement the delegate pattern using protocols, rather than using concrete references like this.