How to move from UIViewController to SKView and vice versa?

302 Views Asked by At

My idea is showing map for few seconds before moving to game scene and the game screens hierarchy is like this:

GameViewController(UIViewController) -> EntryToGameScene (SKView) -> MapController (UIViewController) -> GamePlayGround (SKView)

I'm facing with a problem when moving from MapController to GamePlayGround, to be able to move from controller to scene at first I changed controllers view class to SKView from Storyboard then I added following code ⬇️.After handleDelay() method is running, game scene didMove() method working but the screen stucking on old view (MapController)!? I'm sure game scene didMove() method working because background music starts to play.But why screen is not change?

My code is looks like :

override func viewDidLoad() {
    super.viewDidLoad()
    // Wait for three sec. then move to game scene
    self.perform(#selector(handleDelay), with: nil, afterDelay: 3.0)
}

@objc fileprivate func handleDelay() {

    view.layoutIfNeeded()

    if let view = self.view as! SKView? {
        // Load the SKScene
        let scene = GameScene(size: sceneSize)

        // Set the scale mode
        scene.scaleMode = .aspectFill
        scene.delegate = self
        // Present the scene
        view.presentScene(scene)

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

All answers acceptable.

2

There are 2 best solutions below

1
Coder ACJHP On BEST ANSWER

The solution with removing all subviews from the view then presenting SKScene.

Note: I know its not the best solution but there is no another choise for now, so I changed my code like this:

override func viewDidLoad() {
    super.viewDidLoad()

    self.perform(#selector(handleDelay), with: nil, afterDelay: 3.0)

}

@objc fileprivate func handleDelay() {

    self.view.subviews.forEach {$0.removeFromSuperview()}

    if let view = self.view as! SKView? {
        // Load the SKScene from 'GameScene.swift' and should set it's size
        let scene = SecondGameScene(size: self.view.frame.size)
        // Set the scale mode to scale to fit the window
        scene.scaleMode = .aspectFill

        // Present the scene
        view.presentScene(scene)

        view.ignoresSiblingOrder = true

        view.showsFPS = false
        view.showsNodeCount = false
    }

}

UPDATE : Finally I found right way for moving from SKScene and UIView Controller and vice versa.

For example (Moving from SKScene to UIViewController):

fileprivate func moveToUIViewController(storyBoardId: String) {
    let storyboard = UIStoryboard(name: "Main", bundle: nil)
    let vc = storyboard.instantiateViewController(withIdentifier: storyBoardId)
    vc.view.frame = self.frame
    vc.view.layoutIfNeeded()        

    self.view?.window?.rootViewController?.present(vc, animated: true, completion: nil)
}

(Moving from UIViewController to SKScene):

fileprivate func returnToSKScene() {

    for element in self.view.subviews {
        element.removeFromSuperview()
    }
    self.dismiss(animated: true) {
        if let view = self.view as! SKView? {
            // Load the SKScene
            var scene: SKScene
            scene = AboutScene(size: sceneSize)

            // Set the scale mode
            scene.scaleMode = .aspectFill
            let transition = SKTransition.moveIn(with: .up, duration: 0.2)

            // Present the scene
            view.presentScene(scene, transition: transition)

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

        }
    }
}

Don't forget to change UIViewController view class with "SKView":

  1. Select view the from Storyboard -> Identity inspector -> Custom class.

  2. 2.Select SKView from Class dropdown list.

7
E. Huckabee On

Easy fix. I copied and pasted the code into a test project and I figured out what you had done wrong. You aren't referencing a scene to be presented. You aren't passing in a specific SKView to be presented. You are presenting a generic SKView class.

This code:

override func viewDidLoad() {
    super.viewDidLoad()
    // Wait for three sec. then move to game scene
    self.perform(#selector(handleDelay), with: nil, afterDelay: 3.0)
}

@objc fileprivate func handleDelay() {

    view.layoutIfNeeded()

    if let view = self.view as! SKView? {
        // Load the SKScene
        let scene = GameScene(size: sceneSize)

        // Set the scale mode
        scene.scaleMode = .aspectFill
        scene.delegate = self
        // Present the scene
        view.presentScene(scene)

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

Should look like this:

override func viewDidLoad() {
    super.viewDidLoad()
    // Wait for three sec. then move to game scene
    self.perform(#selector(handleDelay), with: nil, afterDelay: 3.0)
}

@objc fileprivate func handleDelay() {

    view.layoutIfNeeded()

    // Load the view as an SKView to prepare for an SKScene
    if let view = self.view as! SKView? {
        // Load the SKScene from the GameScene file
        let scene = GameScene()
        // Set the scale mode to scale to fit the window
        scene.scaleMode = .aspectFill

        // Present the scene
        view.presentScene(scene)

        view.ignoresSiblingOrder = true

        view.showsFPS = true
        view.showsNodeCount = true
    }
}

Tested and working.