Using extensions can make the life of a programmer much easier.
Here are some extensions that I use regularly.
When I need to create containers, and establish margins, or calculate how much bigger a container should be, to fit something inside of it, I use this function. I noticed I was using SKNode calculateAccumulatedFrame() and then adding margins a lot, so I made this useful function.
Usually I know exactly the width in which I want to fit some text. Even if it is long, I wanted to create a type of label where I just pass a few arguments, and it generates the labels for me. That is what this function does.
The last two functions were created to avoid too many lines of coding, and also when a SKNode has many children, it is a quick method to remove them all by the end of an animation.
extension SKNode{ /** Calculates Accumulated frame of Node + margins defined */ func calculateFrameWithMargins(height:CGFloat, width:CGFloat) -> CGRect{ let oldFrame = self.calculateAccumulatedFrame() let originX = oldFrame.origin.x - width let originY = oldFrame.origin.y - height let newWidth = oldFrame.size.width + (2 * width) let newHeight = oldFrame.size.height + (2 * height) return CGRect(x: originX, y: originY, width: newWidth, height: newHeight) } func createMultilineLabel(message:String, maxWidth:CGFloat, fontSize:CGFloat, aligned:SKLabelHorizontalAlignmentMode){ let mainLabel = SKLabelNode(fontNamed: GameFontNames.inconsolata) mainLabel.fontSize = fontSize mainLabel.horizontalAlignmentMode = aligned mainLabel.verticalAlignmentMode = .top mainLabel.color = SKColor.lightGray let tArray = message.components(separatedBy: " ") var lastFittingLabel:SKLabelNode = mainLabel.copy() as! SKLabelNode lastFittingLabel.text = "" var builtLabels:[SKLabelNode] = [] var lblPosY:CGFloat = 0.0 print("word array") for windex in 0...tArray.count - 1{ let word = tArray[windex] let trialLabel:SKLabelNode = lastFittingLabel.copy() as! SKLabelNode if let previousText = trialLabel.text{ if previousText == "" { trialLabel.text = word }else{ trialLabel.text!.append(" \(word)") } }else{ trialLabel.text = word } let labelWidth = trialLabel.calculateAccumulatedFrame().width if labelWidth > maxWidth{ // does not fit. Add the last one to the array lastFittingLabel.position = CGPoint(x: 0.0, y: lblPosY) builtLabels.append(lastFittingLabel.copy() as! SKLabelNode) // Update height of next Label let heightChange = lastFittingLabel.calculateAccumulatedFrame().height + 2.0 lblPosY -= heightChange // as this word doesn't fit, we need to add it to the next label lastFittingLabel.text = word }else{ // does fit. update lastFittingLabel = (trialLabel.copy() as! SKLabelNode) } // Add the last one if windex == tArray.count - 1{ lastFittingLabel.position = CGPoint(x: 0.0, y: lblPosY) builtLabels.append(lastFittingLabel) } } for label in builtLabels{ self.addChild(label) } } /** Removes all children */ func clearAllChildren(){ // Clear the children if self.children.count > 0{ for child in children{ child.removeFromParent() } } } /** Removes self from parent */ func clearFromParent(){ if let _ = self.parent{ self.removeFromParent() } } }