Need clarity on lifecycles for UIViewController and SKScene

Below is my abbreviated code, the full code does work.
It starts at GameViewController, create GameScene, and GameScene creates an SKSpriteNode.

What's confusing me is the debug output which just tracks where I am in my app (shown in the abbreviated code).

The debug output is at the very bottom of this post.

I wonder about the order of execution, the last debug msg is the end of GameViewController...I would have thought I'd be done with that already.

i.e. How do I know that everything I'm setting in GVC is setup before it's needed in GC?

On a side note, I've tried Googling "SKScene lifecycle" and can't seem to find the proper documentation.


GameViewController
Code Block
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
#if DEBUG
print ("GVC viewDidLoad")
#endif
... create myView (as an SKView)
... add it as a subview of GameViewController
... use myView to get constraints
... and set other properties
myView.isHidden = false
myView.ignoresSiblingOrder = true
myView.showsFPS = true
myView.showsNodeCount = true
myView.showsPhysics = true
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
#if DEBUG
print ("GVC viewWillLayoutSubviews")
#endif
}
func viewDidAppear() {
super.viewDidAppear(true)
#if DEBUG
print ("GVC viewDidAppear")
#endif
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
#if DEBUG
print ("GVC viewDidLayoutSubviews START")
#endif
... get the view.frame
... record some properties in my global variables
... launch GameScene here
if myGlobalVars.gameScene == nil {
let scene = GameScene(size: myView.frame.size )
scene.anchorPoint = CGPoint(x: 0.0, y: 0.0)
scene.backgroundColor = .clear
scene.scaleMode = .aspectFit
myGlobalVars.gameScene = scene
myView.presentScene(scene)
}
#if DEBUG
print ("GVC viewDidLayoutSubviews END")
#endif
}
override var shouldAutorotate: Bool {
return false
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
if UIDevice.current.userInterfaceIdiom == .phone {
return .portraitUpsideDown
} else {
return .all
}
}
override var prefersStatusBarHidden: Bool {
return false
}
}


GameScene
Code Block
class GameScene: SKScene, SKPhysicsContactDelegate {
override func sceneDidLoad(){
super.sceneDidLoad()
#if DEBUG
print ("GC sceneDidLoad")
#endif
}
override func didMove(to view: SKView){
super.didMove(to: view)
#if DEBUG
print ("GC didMove START")
#endif
... load background image
physicsWorld.contactDelegate = self
... create just one SKSpriteNode
createGems(myGemRef: &myGems)
#if DEBUG
print ("GC didMove END")
#endif
}
}



GVC viewDidLoad
GVC viewWillLayoutSubviews
GVC viewDidLayoutSubviews START
GC sceneDidLoad
GC didMove START
SKNode create SKSPriteNode start
SKNode create SKSPriteNode end
GC didMove END
GVC viewDidLayoutSubviews END



Answered by OOPer in 645229022
@SergioDCQ

No it does not. I tried it with both true and false.

The right signature of the lifecycle method viewDidAppear is viewDidAppear(_:Bool).
Code Block
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
#if DEBUG
print ("GVC viewDidAppear")
#endif
}

Your viewDidAppear() has nothing to do with the lifecycle method, and never gets called from iOS.

But it is not your main concern, I guess.


The output you have shown is exactly the same as what I expect from your code. I do not understand what you expected.
Doesn't "GVC viewDidAppear" ever print ?

My understanding is that it is pretty hard to predict the exact sequence, as code may run in different threads.

Doesn't "GVC viewDidAppear" ever print ?

No it does not. I tried it with both true and false.

Code Block
func viewDidAppear() {
super.viewDidAppear(Bool)
#if DEBUG
print ("GVC viewDidAppear")
#endif
}

Accepted Answer
@SergioDCQ

No it does not. I tried it with both true and false.

The right signature of the lifecycle method viewDidAppear is viewDidAppear(_:Bool).
Code Block
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
#if DEBUG
print ("GVC viewDidAppear")
#endif
}

Your viewDidAppear() has nothing to do with the lifecycle method, and never gets called from iOS.

But it is not your main concern, I guess.


The output you have shown is exactly the same as what I expect from your code. I do not understand what you expected.

The output you have shown is exactly the same as what I expect from your code. I do not understand what you expected.

Just wanted to make sure it was folding out the right way. The last problem you helped me kind of gave me an epiphany: The last time I coded I was using the WindowsSDK. I understood how that flowed. I knew how it flowed when one window would start another.

But am totally new to Swift, and iOS. I was able to find the lifecycle of UIViewContrller, but not for SKScene.

 I was able to find the lifecycle of UIViewContrller, but not for SKScene. 

Me neither. So, if you do want to find a reliable source describing the lifecycle of SKScene, frankly I do not know.
It may exist somewhere I do not know, or it may not exist anywhere.

But as far as I understand, --
  • sceneDidLoad() is called when the SKScene did load its contents.

In most cases (in all cases I know), SKScene loads its contents in the initializer, based on the parameters passed.
Thus, in your code, sceneDidLoad() is called when the line let scene = GameScene(size: myView.frame.size ) is executed.
  • didMove(to:) is called when iOS (SpriteKit) did move the scene into an SKView.

iOS moves the scene to an SKView when presentScene(_:) is called.
Meaning didMove(to:) is called as a part of processing presentScene(_:).

Maybe we need to guess what happens from the short descriptions of the methods.
I'm still looking for a better, all-in-one description of the lifecycle SKScene.

Need clarity on lifecycles for UIViewController and SKScene
 
 
Q