Go Back to View Controller from Skscene

Moving from SKScene to ViewController

Replace the code related to currentViewController with this:

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

It should work.
You can also create a segue in Storyboard and call it like this:

self.view?.window?.rootViewController?.performSegue(withIdentifier: "VC", sender: self)

EDIT:

I've tried on one of my app now the next solution and it worked:

let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "VC")
vc.view.frame = rootViewController.view.frame
vc.view.layoutIfNeeded()

UIView.transition(with: window, duration: 0.3, options: .transitionFlipFromRight, animations:
{
window.rootViewController = vc
}, completion: { completed in
// maybe do something here
})

To me it worked, I hope it will solve your case :)

How to get back from SKScene to UIViewController (MenuVC)?

try:

self.view.window!.rootViewController?.dismiss(animated: false, completion: nil)

This should dismiss all views above the rootViewController

edit:

This may work for you as we define the ViewController, make sure in your Storyboard your MenuVC has an Identifier:

let storyboard = UIStoryboard(name: "Main", bundle: nil)

let vc = storyboard.instantiateViewController(withIdentifier: "MenuVC") // Make sure is the same identifier as in the storyboard.

vc.view.frame = (self.view?.frame)!

vc.view.layoutIfNeeded()

UIView.transition(with: self.view!, duration: 0.3, options: .transitionFlipFromRight, animations:

{

self.view?.window?.rootViewController = vc

}, completion: { completed in

})

}

Edit:

It seems the best way to do this, is to host your SKScene class in another view controller. What I suggest is to create another ViewController where your SKScene will be, for example SKViewController. Embed your MenuVC inside a navigationController clicking on it in storyBoard, clicking Editor (from the menu at the top) > Embed in > Navigation Controller. Edit your MenuVC so that instead of opening your Scene, you just segue to the SKViewController, and open the scene there. To do this just create a segue from MenuVC in storyboard, with an identifier "MenuToSKScene" for example, and when you want to open the scene, from your MenuVC just do:

  self.performsegue(with identifier: "MenuToSKScene", sender: self)

Then do whatever you do currently in MenuVC in SKViewController instead to open the scene.

This will send you to the SKViewController, which has your SKScene class. Then when you want to go back to the menu, all you have to do is:

 self.dismiss(animated: true, completion: nil)

or create a segue from SKViewController to MenuVC with identifier "SKSceneToMenu" and call:

self.performsegue(with identifier: "SKSceneToMenu", sender: self)

So essentially, don't open the SKScene from MenuVC, but have a separate viewController SKViewController that you open when you want to open your scene, so that you can jump back to the menu easily.

Getting the UIViewController from an SKScene

Your view controller is:

self.view?.window?.rootViewController

Example:

        let isReady = GADRewardBasedVideoAd.sharedInstance().isReady          
guard let controller = self.view?.window?.rootViewController as? GameViewController else {return}

if isReady {
print("ADMOB: started")
GADRewardBasedVideoAd.sharedInstance().present(fromRootViewController: controller)
}

Switching from a Spritekit Scene back to the storyboard

Initial viewController: an empty viewController with a button to present the GameViewController

GameViewController: the typical GameViewController of the "Hello World" Sprite-kit template. (This is a simplified version of the two scripts as of course you will have more code in yours, however, for the purpose of sharing what I did, this is easier)

My Goal: I wanted to present the first viewController from my SKScene game with the correct deallocation of my scene.

Description: To obtain the result I've extended the SKSceneDelegate class to build a custom protocol/delegate that make the transition from the GameViewController to the first initial controller (main menu). This method could be extended to other viewControllers of your game. This delegate is made use of in the return to main menu function. Make sure to put this function before you call the class for your spritekit script.

The two scripts are shown below. Hope this helps anybody else who had my question.

UIViewController:

import UIKit
import SpriteKit
class GameViewController: UIViewController,TransitionDelegate {
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.view as! SKView? {
if let scene = SKScene(fileNamed: "GameScene") {
scene.scaleMode = .aspectFill
scene.delegate = self as TransitionDelegate
view.presentScene(scene)
}
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
}
}
func returnToMainMenu(){
let appDelegate = UIApplication.shared.delegate as! AppDelegate
guard let storyboard = appDelegate.window?.rootViewController?.storyboard else { return }
if let vc = storyboard.instantiateInitialViewController() {
print("go to main menu")
self.present(vc, animated: true, completion: nil)
}
}
}

Game Script:

import SpriteKit
protocol TransitionDelegate: SKSceneDelegate {
func returnToMainMenu()
}
class GameScene: SKScene {
override func didMove(to view: SKView) {
self.run(SKAction.wait(forDuration: 2),completion:{[unowned self] in
guard let delegate = self.delegate else { return }
self.view?.presentScene(nil)
(delegate as! TransitionDelegate).returnToMainMenu()
})
}
deinit {
print("\n THE SCENE \((type(of: self))) WAS REMOVED FROM MEMORY (DEINIT) \n")
}
}

Go back to view controller from SKScene

Your scene needs to offer a line of communication to your controller to indicate that is finished. You could, for example, create a delegate protocol and corresponding property in your scene. An example:

@protocol TCAMySceneDelegate;

@interface TCAMyScene : SKScene

@property (nonatomic, weak> id<TCAMySceneDelegate> delegate;

@end

@protocol TCAMySceneDelegate <NSObject>
- (void)mySceneDidFinish:(TCAMyScene *)gameScene;
@end

Then, in the .m of your TCAMyScene

- (void)endTheGame {
// Other game-ending code
[self.delegate mySceneDidFinish:self];
}

In your view controller, set itself as the delegate for your scene and implement the method:

- (IBAction)startGame:(id)sender {
// Other code

TCAMyScene *theScene = [TCAMyScene sceneWithSize:skView.bounds.size];
theScene.scaleMode = SKSceneScaleModeAspectFill;
theScene.delegate = self;

// Other code
}

- (void)mySceneDidFinish:(TCAMyScene *)myScene {
// logic for dismissing the view controller
}

dismiss SKScene go back to UIKit Menu

I agree with Whirwind comment's: why mixing two different frameworks and complicate your life when you can use one viewController only to do all your game?

Anyway, according to your storyboard screenshot,there are 2 viewControllers and you can go to the second viewController (and to the GameScene) only if you press a button.

There are two things to do: deallocate the current SKScene (in your case the GameScene) and present the "initial view controller" or your MenuViewController.

To do it, I use the "Hello world" Sprite-kit template with a protocol/delegate approach to extend the SKSceneDelegate class. As you can see we able to dismiss the scene (presenting nil) and call an external method on the GameViewController to present the MainViewController

To be sure that both of these operations go to be successful I use also two print just for debug:

GameViewController:

import UIKit
import SpriteKit
class GameViewController: UIViewController,TransitionDelegate {
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.view as! SKView? {
if let scene = SKScene(fileNamed: "GameScene") {
scene.scaleMode = .aspectFill
scene.delegate = self as TransitionDelegate
view.presentScene(scene)
}
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
}
}
func returnToMainMenu(){
let appDelegate = UIApplication.shared.delegate as! AppDelegate
guard let storyboard = appDelegate.window?.rootViewController?.storyboard else { return }
if let vc = storyboard.instantiateInitialViewController() {
print("go to main menu")
self.present(vc, animated: true, completion: nil)
}
}
}

GameScene:

import SpriteKit
protocol TransitionDelegate: SKSceneDelegate {
func returnToMainMenu()
}
class GameScene: SKScene {
override func didMove(to view: SKView) {
self.run(SKAction.wait(forDuration: 2),completion:{[unowned self] in
guard let delegate = self.delegate else { return }
self.view?.presentScene(nil)
(delegate as! TransitionDelegate).returnToMainMenu()
})
}
deinit {
print("\n THE SCENE \((type(of: self))) WAS REMOVED FROM MEMORY (DEINIT) \n")
}
}

Output:

Sample Image

Segue in SKScene to UIViewController

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 {
weak 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(){
viewController?.performSegueWithIdentifier("menu", sender: vc)
}


Related Topics



Leave a reply



Submit