3
votes

I have an SKNode with a number of child SKSpriteNodes. A simplified example:

var parentNode = SKNode()
var childNode1 = SKSpriteNode()
var childNode2 = SKSpriteNode()

self.addChild(parentNode)
parentNode.addChild(childNode1)
parentNode.addChild(childNode2)

I want to run a colorizeWithColor action on all of these children. When I run the action on parentNode, there's no effect.

I can't use enumerateChildNodesWithName on the parent, because many of its children already have names I'm using.

Is there a way of looping through all children of parentNode, in order to run a single action on all of them?

4
It turns out the native .children call is unusable in normal performance situations. You have to just keep your own list. (As if 2017.)Fattie

4 Answers

18
votes

You can simply enumerate parentNode.children:

for child in parentNode.children as! [SKNode] {
    // ...
}

If necessary, check each child if it is actually a SKSpriteNode:

for child in parentNode.children {
    if let spriteNode = child as? SKSpriteNode {
        // ...
    }
}

As of Swift 2 (Xcode 7), enumeration and optional cast can be combined with to a for-loop with a case-pattern:

for case let child as SKSpriteNode in parentNode.children {
    // ...
}
3
votes

As of 2017..

The code is indeed just

for case let child as SKSpriteNode in parentNode.children {
    // ...
}

But.

Performance is incredibly bad: it is not usable.

You will have to keep your own list.

Naturally, if you are creating a number of "tanks" or "spaceship" you will naturally keep those in a list anyway. It's the only viable approach if you need to iterate.

I ran some test code like this ...

    // try Apple's native .children call...
     
    for case let s as SomeSpriteClass in children { k += 1 }
    let t1 = a.microseconds()
    
    // simply use your own list...
     
    let b = Date()
    for s in spaceships { k += 1 }
    let t2 = b.microseconds()
    
    print("\(t1) \(t2)")

it is 3 or 4 times faster, if you just iterate over your own list.

Typical output on an iPad ...

939 43
140 33
127 33
117 37
109 30
126 33
127 33
109 29
96 30
96 29
99 30
97 30
97 30

(In particular, as you'd expect, the built-in enumeration takes forever the "first" time.)

Given the performance, in practice you must to keep your own list of sprites and use that list.

1
votes

You can get an array of the children by accessing the parent's children-attribute, and then loop over them and run colorizeWithColor on them.

let children = parentNode.children
for child in children {
    // Do something.
}
0
votes

I needed to iterate through a SCNNode to look for a custom SCNNode subclass that I created. Even though this question is for a SKNNode @MartinR's answer was the only answer that I could find that put me in the right direction. I had to tweak it but this is what I used:

for child in parentNode.childNodes {

    if let myCustomNode = child as? MyCustomNode {
        // ...
    }                        
}