简体   繁体   中英

Unable to segue from Spritekit SKScene to another UIViewController, XCODE8/Swift

I'm completely stuck at trying to perform a segue out of the 5th and final scene of my SpriteKit game to another View Controller in the project(not the GameViewController, nor the root view controller).

I tried running self.view!.window!.rootViewController!.performSegueWithIdentifier("finalSegue", sender: self), from my finalScene, but it literally does nothing (the line gets triggered, it reads the right ViewController - i check by "print(self.view!.window!.rootViewController!)" console prints "segue read" as I instructed it, right after the segue command, as a check, the segue identifier is correct, but nothing happens).

Have tried calling a method that performs the segue from the GameViewController ( the view controller from which I am launching the view of the 5 SKScenes), I get "unexpectedly found nil while unwrapping an optional value". Tried performing the segue from the final scene ("finalScene.swift"), same error.

Have tried everything and all relevant solutions in other questions in the forum, as well as all combinations of nil/self/viewController in the "sender:" field of the performSegue method, to no avail. Here is the code that I am trying to make work which gives "unexpectedly found nil while unwrapping an optional value", pointing at the viewController var, but giving uncomprehensible debugging when loaded both on the device and on the simulator. It seems "nil" passes into the viewController var I am declaring, instead of the original GameViewController?

All my segue identifiers are correct in Storyboard, everything checked multiple times...What am I doing wrong? Should I do something different, given its the 5th SKScene and not the 1st (as in other solutions)? The segue into the SKScenes by segueing into the GameViewController from another UIViewController works fine - its the exit out of them that does not work. Many thanks for any help, completely stuck here!

Here is my relevant code:

In my GameViewController (UIViewController that launches my 5 consecutive SKScenes):

    class GameViewController: UIViewController {
        let myScene = finalScene()
        override func viewDidLoad() {
            super.viewDidLoad()

    if let view = self.view as! SKView? {

//this is the 1st out of 5 SKScenes in the project
                let scene = GameScene(size: CGSize(width: 2048, height: 2742))

//this is the 5th out of 5 scenes, that I am trying to trigger the segue out of
                myScene.viewController = self
                view.presentScene(scene)
                view.ignoresSiblingOrder = true 
        }
    }
    }

Im my 5th scene, finalScene:

class finalScene: SKScene {

    var viewController: GameViewController!

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {

        for touch: AnyObject in touches{

            let positionOfTouch = touch.location(in: self)
            let tappedNode = atPoint(positionOfTouch)
            let nameOfTappedNode = tappedNode.name
            if nameOfTappedNode == "continue" {

 self.viewController.performSegue(withIdentifier: "finalSegue", sender: self)            

}
        }
    }

Just for anyone who might come across this, I solved it by using notification center. Ie added a notification observer on the parent view controller of the game scenes (which calls a function that performs the segue), and at the end point in the game where I wanted to do the segue, I posted to that notification, and it works great.

adding observer in ViewDidLoad of UIViewController:

    override func viewDidLoad() {
        super.viewDidLoad()

            NotificationCenter.default.addObserver(self, selector: #selector(GameViewController.doaSegue), name: NSNotification.Name(rawValue: "doaSegue"), object: nil)
}

    func doaSegue(){
        performSegue(withIdentifier: "toNext", sender: self)
        self.view.removeFromSuperview()
        self.view = nil
    }

And then calling it from within the game SKScene:

NotificationCenter.default.post(name: NSNotification.Name(rawValue: "doaSegue"), object: nil)

You are calling the segue on the root viewController. I think that is the problem. You need to call the segue on the scene's viewController instead (where I am assuming you have created the segue, hence it is not being found on the root viewController).

Now the problem is that an SKScene does not have direct access to it's viewController, but just the view in which it is contained. You need to create a pointer to it manually. This can be done by creating a property for the SKScene:

class GameScene: SKScene {
    var viewController: UIViewController?
    ...
}

Then, in the viewController class, just before skView.presentScene(scene)

scene.viewController = self

Now, you can access the viewController directly. Simply call the segue on this viewController:

func returnToMainMenu(){
    self.viewController.performSegueWithIdentifier("menu", sender: vc)
}

Found this method to work just as well. But I do prefer the method mentioned above since it's fewer lines. I do wonder which is better for performance...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM