i have a general question about view controllers and how to handle them in a clean way when i develop a SpriteKit based game.
What i did so far:
- Use storyboard only for defining view controllers
- SKScene's are presented in each view controller (Home, LevelSelection, Game) by presentScene
- in each view controller i call performSegueWithIdentifier with the identifier i defined in the storyboard between the view controllers
- all the content i show programmatically using SKSpritenode etc. on the SKScene's
- on the storyboard i only have view controllers with segue relations and identifiers defined
- all the stuff i do in viewDidDisappear is because it seems to be the only way to get my SKScene deinited correctly
My problems are:
- everytime i segue to another view, my memory raises, because the view controller is re-initialized, the old one keeps staying in the stack
- it is not clear for me how to handle the segue's between the view controllers, on some tutorial pages i see people using the navigation controller, others are using strong references of some view controllers and using the singleton pattern for the view controller in order to decide either to init the view controller or just show it
- my view controllers are not deiniting, i understand my home view can't because it is the initial one, but since ios is reiniting it anyways, why then not unloading it?
What is the correct way for a Swift based game using SpriteKit to handle the view controller? Below you can see my initial view controller (Home) showing an SKScene with a simple play button which calls the play() function to segue to the levelselection
import UIKit
import SpriteKit
class Home : UIViewController {
private var scene : HomeScene!
override func viewDidLoad() {
print(self)
super.viewDidLoad()
self.scene = HomeScene(size: view.bounds.size)
self.scene.scaleMode = .ResizeFill
let skView = view as! SKView
skView.showsFPS = true
skView.showsNodeCount = true
skView.ignoresSiblingOrder = true
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(play), name: Constants.Events.Home.play, object: nil)
skView.presentScene(self.scene)
}
override func viewDidDisappear(animated: Bool) {
super.viewDidDisappear(animated)
let v = view as! SKView
self.scene.dispose()
v.presentScene(nil)
NSNotificationCenter.defaultCenter().removeObserver(self)
self.scene = nil
self.view = nil
print("home did disappear")
}
func play() {
self.performSegueWithIdentifier("home_to_levelselection", sender: nil)
}
deinit {
print("Home_VC deinit")
}
}
Your way seems very complicated to essentially present 3 scenes. Its not what you are supposed to do for SpriteKit games, you only really need 1 view controller (GameViewController).
Load your first scene from GameViewController (e.g HomeScene) and nothing else. Create your playButton and other UI directly in HomeScene. Use SpriteKit APIs for your UI (SKLabelNodes, SKNodes, SKSpriteNodes etc).
You should never really use UIKit (UIButtons, UILabels) in SpriteKit. There are some exceptions to this, like maybe using UICollectionViews for massive level select menus, but basic UI should be done with SpriteKit APIs.
There is plenty tutorials to google on how to create sprite kit buttons, how to use SKLabelNodes etc. Xcode has a SpriteKit level editor so you can do all that visually similar to storyboards.
Than from HomeScene transition to the LevelSelect Scene and than to the GameScene and vice versa. Its super easy to do.
I strongly recommend you scratch your storyboard and ViewController approach, and just use different SKScenes and 1 GameViewController.
Hope this helps
Go to the segues and use Show Detail Segues anywhere that you don't want the previous view controller to be kept in the stack. Keep in mind that you will have to reinitialize everything appropriately in those view controllers whenever you do return to them.
If you pay attention,
viewDidAppear
loads every time that you see the view appear, while with your current setup,viewDidLoad
would only be called initially and if you returned to theviewController
, onlyviewDidAppear
would be called.When you use a segue to transition out of a
viewController
,prepareForSegue
is called, butdeinit()
is only called when you use a show detail segue (or custom segue with those specific properties about it), because the view, like you said, is loaded into memory so it can be retrieved easier.