Hi I have a spriteKit game set up where everytime the user dies Ill get a pop up ViewController with Try again button and some iAds set up.
I have a segue to the viewController and an unwind segue from the VC to the gameViewController.
When I call the unwind func I reinitialize the scene for different reasons. My question is, am I creating view over view which will eventually lead to a crash or am I correctly reinitializing the scene. I took the code from viewDidLoad and put it into a function called "setUp()" and I call that function from the unwindSegue.
check it out: (all in GameViewController)
var currentLevel: Int!
var gameScene = GameScene()
override func viewDidLoad() {
super.viewDidLoad()
currentLevel = gameScene.currentLevel
setUp()
}
#IBAction func perpareForUnwind(unwindSegue: UIStoryboardSegue) {
setUp()
}
func setUp() {
if let scene = GameScene.level(currentLevel) {
// Configure the view.
let skView = self.view as SKView
skView.showsFPS = true
skView.showsNodeCount = true
/* Sprite Kit applies additional optimizations to improve rendering performance */
skView.ignoresSiblingOrder = true
/* Set the scale mode to scale to fit the window */
scene.scaleMode = .AspectFill
skView.presentScene(scene)
scene.viewController = self
}
}
You are not creating a new view controller if you are using the unwind segue correctly. From what it looks like that is the case. If you are still not confident you can log out println(\(self)) and make sure of it (My swift is rusty but I think that is how you log it out).
Your question title asks about view controllers but your question info asks about creating additional views. This isn't an issue either. When you do
let skView = self.view as SKView
You are getting the current view of the view controller and just casting it as an SKView. You are not recreating another view at all.
Hopefully that helps.
Related
I know this question has been asked many times, however most aren't properly answered and do not follow the same set up as mine.
I have two UIViewControllers, GameViewController and MenuViewController. The MenuViewController uses a storyboard and UIKit. The GameViewController loads an SKScene. When the app starts it opens the MenuViewController. I have already made a button that segues to the Game on the Menu in the storybaord. That works, but now I am trying to do the opposite and return back to the Menu while in the Game. I have tried to use code that has answered similar questions but none seems to work.
Here is my GameViewController that I messed around with:
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.view as! SKView?{
if let scene = SKScene(fileNamed: "GameScene"){
let gameScene = scene as! GameScene
gameScene.gameViewControllerDelegate = self
gameScene.scaleMode = .aspectFill
view.presentScene(gameScene)
}
view.showsFPS = true
view.showsNodeCount = true
view.ignoresSiblingOrder = true
// Do any additional setup aft)er loading the view.
}
}
func callMethod(inputProperty:String){
print("inputProperty is: ",inputProperty)
}
And the code I added to my GameScene:
class GameScene: SKScene, SKPhysicsContactDelegate {
weak var gameViewControllerDelegate:GameViewControllerDelegate?
override func didMove(to view: SKView) {
gameViewControllerDelegate?.callMethod(inputProperty: "call game view controller method")
QUESTION: How can I dismiss a ViewController from my GameScene.swift ?
SITUTATION: I have 2 VCs in my SpriteKit Game, like so:
ViewController.swift ----Press Play-----> GameViewController
When the player loses, I want to dismiss the GameViewController so the player can press play again. I check for the player's loss in my GameScene.swift and would like to dismiss the GameVC from there.
N.B.: Googled this without success.
WHAT I TRIED:
1) Creating a gameVC instance in my GameScene.swift and dismissing it like so:
let gameVC = GameViewController()
gameVC.dismissViewController(false,completion: nil)
2) Doing:
self.view.window!.rootViewController?.dismissViewControllerAnimated(false, completion: nil)
Those don't work for obvious reasons ^^
You don't want to "grab" the existing instance: https://pragprog.com/articles/tell-dont-ask
You need to either hand GameScene a reference to the view controller so it can dismiss it, or use the delegate pattern to communicate backwards to a controlling object that the VC should be dismissed/dismiss itself.
A simple example… you can add a GameViewController property to GameScene, then dismiss the VC at the appropriate time:
class GameScene: SKScene {
var gameVC: GameViewController?
func gameDidEnd() {
gameVC?.dismissViewControllerAnimated(true) {
// if desired, do any cleanup after the VC is dismissed
}
}
}
Then, just set this property when creating the GameScene object in the first place:
if let gameScene = GameScene(fileNamed: "MyScene") {
gameScene.gameVC = someGameVC
}
This simple approach will tightly couple GameScene and GameViewController, making it a bit more difficult if you ever want to use one of these objects without the other. But for this simple use case, it may be fine.
I've follow some of your discussion. I want to add some code, because usually I prefeer to work with one ViewController or two and many SKScene and SKNode, but in this case could be useful to have a currentViewController reference:
class MyModelScene: SKScene {
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
var currentViewController : MyModelViewController! = MyModelViewController()
// MyModelViewController is a customized UIViewController
override func didMoveToView(view: SKView) {
super.didMoveToView(view)
print("---")
print("∙ \(NSStringFromClass(self.dynamicType))")
print("---")
}
}
class Level1Scene: MyModelScene {
...
}
In the UIViewController:
class PreloadViewController: MyModelViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let scene = Level1Scene(fileNamed:"Level1Scene") {
// Configure the view.
let skView = self.view as! SKView
skView.showsFPS = true
skView.showsPhysics = true
skView.showsNodeCount = true
skView.ignoresSiblingOrder = true
/* Set the scale mode to scale to fit the window */
scene.scaleMode = .ResizeFill
scene.currentViewController = self
skView.presentScene(scene)
}
}
}
With this code , you've always a currentViewController reference in your SKScene and you can check if it's the correct viewController you want to dismiss or not.
I wrote an iOS-App that uses multiple Views. Since I did not want to use the storyboard, I created a set of Views (xib files) and corresponding ViewControllers (swift files that extend UIViewController).
The views are created with all "Simulation Metrics" set to inferred, "Use Auto Layout" is on and "Use Size Classes" is also on. For every element, constraints are set, no errors.
In func application(application: UIApplication, didFinishLaunchingWithOptions ...) in AppDelegate.swift, I initiate my ViewControllers, e.g. by
connectVC = ConnectViewController(nibName: "ConnectView", bundle: nil)
On user actions (e.g. button click) and network actions (e.g. game started), I switch views programmatically with an animation by changing the window.rootViewController in AppDelegate.swift, exactly like here but in Swift.
So far, so good. But whenever I show a view for the first time, the view appears with the scaling/size just like in my Xcode interface builder, then resizes about half a second to a second later to the correct size of device's screen. This happens in the iOS Simulator as well as on my real iPhone.
The views use quite a few images in UIImageViews, but reducing the resolution or amount of pictures does not change that behaviour.
I would like to speed up that resize, ideally the users should not see any resizing of elements in my view. How can I do that?
This happened to me. First go into your GameViewController that starts up with the app. Then find this code:
if let scene = GameScene(fileNamed:"GameScene") {
// Configure the view.
let skView = self.view as! SKView!
/* Sprite Kit applies additional optimizations to improve rendering performance */
skView.ignoresSiblingOrder = true
skView.showsFPS = false
skView.showsNodeCount = false
/* Set the scale mode to scale to fit the window */
scene.scaleMode = .ResizeFill
skView.presentScene(scene)
}
Change all of that code to this:
if let skView = self.view as! SKView! {
let scene = GameScene(size: self.view.bounds.size)
skView.ignoresSiblingOrder = true
skView.showsFPS = false
skView.showsNodeCount = false
scene.scaleMode = .ResizeFill
scene.size = skView.bounds.size
skView.presentScene(scene)
}
That should work, if you change GameScene to what ever your first scene is.
The problem was that I was using the ViewController chain incorrectly. Simply changing the window.rootViewController and doing a UIView.transitionFromView is not the indented way to deal with view switching.
So I created a SuperViewController that contains instances of all other ViewControllers and use the two functions below to switch between views. Now works like a charm.
The Creating Custom Container View Controllers article and also View Controller
Programming Guide for
iOS from Apple helped me quite a lot.
func setViewController(vc: UIViewController) {
self.addChildViewController(vc)
vc.view.frame = self.view.bounds
self.view.addSubview(vc.view)
vc.didMoveToParentViewController(self)
self.currentViewController = vc
}
func switchViewController(from: UIViewController, to: UIViewController) {
from.willMoveToParentViewController(nil)
self.addChildViewController(to)
to.view.frame = self.view.bounds
self.transitionFromViewController(from, toViewController: to, duration: 0.5, options: .TransitionCrossDissolve, animations: nil) { finished in
from.removeFromParentViewController()
to.didMoveToParentViewController(self)
self.currentViewController = to
}
}
I'm trying to understand what is happening in here. I want to add a table view controller in a game to simply present the daily score. Now, that view is accessible from main menu by touching a SpriteNode.
if self.nodeAtPoint(location) == statistics
{
let vc = self.view?.window?.rootViewController
vc?.performSegueWithIdentifier("Statistics", sender: self)
}
Everything goes great for the first time. The table appears and is showing what it needs to. Now, from the tableview, by swiping to right I'm going back to my main menu:
if swipeGesture.direction == UISwipeGestureRecognizerDirection.Right
{
performSegueWithIdentifier("back", sender: self)
}
When I'm trying again to go again to the statistics tableview the app is blocked and I receive that warning: Attempt to present * on * whose view is not in the window hierarchy!.
I guess that when is coming back from the statistics table, is going to the GameViewController.swift and only after that the MainMenu is presented.
if let scene = MainMenu.unarchiveFromFile("GameScene") as? MainMenu {
// Configure the view.
let skView = self.view as SKView
skView.showsFPS = false
skView.showsNodeCount = false
/* Sprite Kit applies additional optimizations to improve rendering performance */
skView.ignoresSiblingOrder = true
/* Set the scale mode to scale to fit the window */
scene.scaleMode = .AspectFill
skView.presentScene(scene)
}
Can anyone help? Many thanks.
Inside my main UIViewController, I'm defining two SKScenes. I want to present only gameScene at first, and then present uiScene later, triggered by an action in gameScene (hitting the pause button).
The problem is that skView, the view containing both scenes, is not recognized outside of the UIViewController.
The code is below. Any help would be appreciated.
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let skView = view as SKView
let gameScene = GameScene(size: view.bounds.size)
gameScene.scaleMode = .ResizeFill
skView.presentScene(gameScene)
let uiScene = UIScene(size: view.bounds.size)
uiScene.scaleMode = .ResizeFill
uiScene.backgroundColor = UIColor.clearColor()
//skView.presentScene(uiScene) // I want to present this line from within gameScene.
}
}
let reveal = SKTransition.flipHorizontalWithDuration(0.5)
let scene = uiScene(size: size)
self.view?.presentScene(scene, transition:reveal)
You can transition from one scene to another from within the scene. My code is pulled from http://www.raywenderlich.com/84434/sprite-kit-swift-tutorial-beginners in the last section.
Remember to import the header of the scene you are transitioning TO in the scene you are transitioning FROM.