Reputation: 13583
I am rendering a SKScene as a mere grid of node where I project a data structure made by a greater grid of journals.
While asked to render a node at (i,j) position in the grid, the code looks for media (texture) availability, and in that case, adds a SKSpriteNode. But if not immediately avail, requests the texture from a remote server (in a thread of course). As a consequence, because it is not immediately available, the code creates a fallback SKShapeNode in order to make it visible by the user while downloading the texture.
When and if the texture is received, the replaces the fallback SKShapeNode with the SKSpriteNode with a newly created SKSpriteNode made of the received texture.
Each node is named against its position in the grid, aka. "((i),(j))".
Apart of that, I handle a pinch gesture that is responsible for rearrange nodes position depending on the scale educated by the pinch (nut not applying a scale to the scene).
All is ok on the paper, but unfortunately, I didn't find a way to avoid mutating the sprite node children list while enumerating.
What should I do?
for i in 0..<tilesh {
for j in 0..<tilesw {
let row = i
let col = j + band * tilesw
if let journal = Journal.journalForLevel(level, row: row, col: col) {
// --- ask for the journal cover, but if not straght availble, fetch from cache or remote server
let _ = journal.getCover {
// If completion block is invoked, that mean the cover has been fetch from a cache level
(journal: Journal) in
// If cover is now available, we simply creat the tile as a SKNode
createteOrUpdateJournalNode(panel, journal: journal, row: i, col: j)
}
// --- Draw journal at each tile if cover already avail, just draw it
createteOrUpdateJournalNode(panel, journal: journal, row: i, col: j)
}
// --- No journal available
else {
createteOrUpdateJournalNode(panel, journal: nil, row: i, col: j)
print("No journal avail for band=\(band) at row=\(row), col=\(col)")
}
} // each j
} // each i
func createteOrUpdateJournalNode(parent: Panel, journal: Journal?, row: Int, col: Int) {
var node : SKNode
let alpha:CGFloat = 1 //0.5 + 0.5 * CGFloat((band+1)/panels.count)
if let journal = journal {
if let _ = journal.cover {
let cover = prepareCover(journal)
let texture = SKTexture(image: cover)
let n = SKSpriteNode(texture: texture)
node = n
n.anchorPoint = CGPoint(x: 0, y: 0)
}
else {
let n = SKShapeNode(rect: CGRect(x: 0, y: 0, width: w, height: h))
node = n
let c = 0.5 + CGFloat(arc4random() % 127) / 127
n.fillColor = UIColor(red: c, green: c, blue: 1, alpha: alpha)
n.strokeColor = UIColor.blueColor()
n.lineWidth = 4
}
}
else {
let n = SKShapeNode(rect: CGRect(x: 0, y: 0, width: w, height: h))
node = n
let c = 0.5 + CGFloat(arc4random() % 127) / 127
n.fillColor = UIColor(red: 1, green: c, blue: c, alpha: alpha)
n.strokeColor = UIColor.redColor()
n.lineWidth = 4
}
let name = "\(band)-\(row)-\(col)"
node.name = name
node.position = CGPoint(x: CGFloat(col) * w * timeScale, y: CGFloat(row) * h)
if let n = parent.childNodeWithName(name) {
//n.runAction(SKAction.removeFromParent())
print ("---- should prune node from band \(band) at \(name)")
}
print(">>>> Creating node \(name)")
parent.addChild(node)
print("<<<<")
}
Upvotes: 0
Views: 215
Reputation: 13583
I solved the issue by founding that one can safely mutate the node list in the SpriteKit update() func as follows:
// SpriteKits gameloop function
override func update(currentTime: CFTimeInterval) {
// Mutate list
for p in panels {
p.addNodes()
}
}
p refers to an array of Panel (see the Panel class later in the answer).
I prepare the additional nodes from the rendering loop in my slightly modified version of createOrUpdateJounralNode() as follows:
let node = createOrUpdateJournalNode(nil, row: i, col: j)
panel.addNode(node)
Where panel.addNode() simply refers to the code snippet as follows:
class Panel {
var addNodesList = [SKNode]()
func addNode(node: SKNode) {
addNodesList.append(node)
}
func addNodes() {
if addNodesList.count > 0 {
for n in addNodesList {
if let name = n.name {
if let n = childNodeWithName(name) {
n.removeFromParent()
print("---- removing node \(name)")
}
}
addChild(n)
print("++++ adding node \(name)")
}
addNodesList = [SKNode]()
}
}
}
The slightly modified version of createOrUpdateJournalNode() is as follows:
func createOrUpdateJournalNode(journal: Journal?, row: Int, col: Int) -> SKNode {
// same code than before except we don't add child to parent, but simply return the node
return node
}
Upvotes: 0
Reputation: 42133
Perhaps you could avoid the mutation problem altogether by using a SKSpriteNode as the temporary placeholder and only assigning a new texture when you receive the data. that way, the same object/instance is used and your enumeration will not be affected
Upvotes: 1