Search code examples
sprite-kitskspritenode

What's the "didMove#to" for SKSpriteNode?


Of course, in a scene...

class DotScene: SKScene {
    
    override func didMove(to view: SKView) {
        
        print("this scene is now in place...")

you know the scene is in place when didMove#to is called.

(It's just like ViewDidAppear, you could say.)

However

I have no clue how to know that a sprite has been added to a scene.

class Spaceship: SKSpriteNode {
    
    func wtf() {
        
        this sprite has just been added to a scene
        (such as with .childNode)
        this sprite is now in place in the scene ...

There simply - has to be - a call that alerts you that a node has appeared on the scene successfully.

What is it ?


Solution

  • In SpriteKit there is no way to detect inside of a custom sprite class when a node is added to a scene. This is has been left out because you have control when a sprite is added to a scene via addChild or moveToParent.

    Spritekit does not conform to an MVC architecture, where as UIKit does. One of the reasons didMoveToView exists is because the purpose of the View is to be an output display only. The controller is responsible for handling the code behind for the View. Now the view controller can be used to call presentScene from view, but if we are transitioning, we really have no idea at what point the scene is officially attached to the view. (There are other reasons besides this, I am just giving one example)

    Now to get around this issue, you may be able to implement Key Value Observing (KVO), and listen to when scene gets set. To do this, simply do:

    override init()
    {
        super.init()
        addObserver(self, forKeyPath: #keyPath(SKNode.scene), options: [.old, .new, .initial], context: nil)
    }
    
    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) 
    {
        if keyPath == #keyPath(SKNode.scene) && self.scene != nil 
        {
            didAttachToScene()
        }
    }
    
    func didAttachToScene()
    {
    }
    
    deinit 
    {
        removeObserver(self, forKeyPath: #keyPath(SKNode.scene))
    }
    

    Now, I have not tested this, I am currently in a windows environment, but if this does not work, you can listen to when a node is attached to a parent (which I have actually done with KVO so I know it is possible), and infer if we are on a scene that way since a node must have a parent to even qualify being on a scene.

    override init()
    {
        super.init()
        addObserver(self, forKeyPath: #keyPath(SKNode.parent), options: [.old, .new, .initial], context: nil)
    }
    
    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) 
    {
        if keyPath == #keyPath(SKNode.parent) && self.scene != nil 
        {
            didAttachToScene()
        }
    }
    
    func didAttachToScene()
    {
    }
    
    deinit 
    {
        removeObserver(self, forKeyPath: #keyPath(self.parent))
    }