├── demo.gif ├── demo2.gif ├── SKAdvancedLabelNode.xcodeproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ ├── admin.xcuserdatad │ │ └── UserInterfaceState.xcuserstate │ │ └── alex.xcuserdatad │ │ └── UserInterfaceState.xcuserstate ├── xcuserdata │ ├── admin.xcuserdatad │ │ └── xcschemes │ │ │ └── xcschememanagement.plist │ └── alex.xcuserdatad │ │ ├── xcschemes │ │ └── xcschememanagement.plist │ │ └── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist └── project.pbxproj ├── LICENSE ├── SKAdvancedLabelNode ├── GameViewController.swift ├── Info.plist ├── Base.lproj │ ├── Main.storyboard │ └── LaunchScreen.storyboard ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── AppDelegate.swift ├── GameScene.swift └── Source │ └── SKAdvancedLabelNode.swift └── README.md /demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aornano/SKAdvancedLabelNode/HEAD/demo.gif -------------------------------------------------------------------------------- /demo2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aornano/SKAdvancedLabelNode/HEAD/demo2.gif -------------------------------------------------------------------------------- /SKAdvancedLabelNode.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SKAdvancedLabelNode.xcodeproj/project.xcworkspace/xcuserdata/admin.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aornano/SKAdvancedLabelNode/HEAD/SKAdvancedLabelNode.xcodeproj/project.xcworkspace/xcuserdata/admin.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /SKAdvancedLabelNode.xcodeproj/project.xcworkspace/xcuserdata/alex.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aornano/SKAdvancedLabelNode/HEAD/SKAdvancedLabelNode.xcodeproj/project.xcworkspace/xcuserdata/alex.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /SKAdvancedLabelNode.xcodeproj/xcuserdata/admin.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | SKAdvancedLabelNode.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /SKAdvancedLabelNode.xcodeproj/xcuserdata/alex.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | SKAdvancedLabelNode.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2018 Alessandro Ornano 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /SKAdvancedLabelNode/GameViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GameViewController.swift 3 | // SKAdvancedLabelNode 4 | // 5 | // Created by Alessandro Ornano on 14/01/2018. 6 | // Copyright © 2018 Alessandro Ornano. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SpriteKit 11 | import GameplayKit 12 | 13 | class GameViewController: UIViewController { 14 | 15 | override func viewDidLoad() { 16 | super.viewDidLoad() 17 | print("---") 18 | print("∙ \(type(of: self))") 19 | print("---") 20 | guard let view = self.view as! SKView? else { return } 21 | view.ignoresSiblingOrder = true 22 | view.showsFPS = true 23 | view.showsNodeCount = true 24 | view.showsPhysics = true 25 | view.showsDrawCount = true 26 | 27 | let scene = GameScene(size:view.bounds.size) 28 | scene.scaleMode = .aspectFill 29 | view.presentScene(scene) 30 | } 31 | 32 | override var shouldAutorotate: Bool { 33 | return false 34 | } 35 | 36 | 37 | override func didReceiveMemoryWarning() { 38 | super.didReceiveMemoryWarning() 39 | // Release any cached data, images, etc that aren't in use. 40 | } 41 | 42 | override var prefersStatusBarHidden: Bool { 43 | return true 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /SKAdvancedLabelNode/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UIStatusBarHidden 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationLandscapeLeft 36 | UIInterfaceOrientationLandscapeRight 37 | 38 | UISupportedInterfaceOrientations~ipad 39 | 40 | UIInterfaceOrientationLandscapeLeft 41 | UIInterfaceOrientationLandscapeRight 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /SKAdvancedLabelNode.xcodeproj/xcuserdata/alex.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 20 | 21 | 22 | 24 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /SKAdvancedLabelNode/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /SKAdvancedLabelNode/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /SKAdvancedLabelNode/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | } 88 | ], 89 | "info" : { 90 | "version" : 1, 91 | "author" : "xcode" 92 | } 93 | } -------------------------------------------------------------------------------- /SKAdvancedLabelNode/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // SKAdvancedLabelNode 4 | // 5 | // Created by Alessandro Ornano on 14/01/2018. 6 | // Copyright © 2018 Alessandro Ornano. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## SKAdvancedLabelNode 2 | >SKLabelNode with single-handling-chars in Swift 3 | 4 | [![Language](http://img.shields.io/badge/language-swift-brightgreen.svg?style=flat 5 | )](https://developer.apple.com/swift) 6 | [![Build Status](https://travis-ci.org/Alamofire/Alamofire.svg?branch=master)](https://travis-ci.org/Alamofire/Alamofire) 7 | [![Platform](http://img.shields.io/badge/platform-ios-blue.svg?style=flat 8 | )](https://developer.apple.com/iphone/index.action) 9 | [![License](https://img.shields.io/cocoapods/l/BadgeSwift.svg?style=flat)](/LICENSE) 10 | 11 | Sequentially bouncing zoom animation: 12 | 13 | ![sequentially bouncing zoom animation](https://github.com/aornano/SKAdvancedLabelNode/blob/master/demo.gif) 14 | 15 | Shake: 16 | 17 | ![shake](https://github.com/aornano/SKAdvancedLabelNode/blob/master/demo2.gif) 18 | 19 | **SKAdvancedLabelNode** is a library written in Swift to improve the actual SpriteKit ```SKLabelNode``` class. It contains an array of each single char of your text re-builded as a ```SKLabelNode``` with one char to permit to animate or handling a single char of your label, few chars or all chars of text. This opens up the possibilities to create ```CGPath``` where your char comes for example from different points of screen using for example ```UIScreen.main.bounds.size``` as reference to adjust points.. 20 | 21 | - [Features](#features) 22 | - [ToDo](#todo) 23 | - [Requirements](#requirements) 24 | - [Communication](#communication) 25 | - [Installation](#installation) 26 | - [Usage](#usage) 27 | - [License](#license) 28 | 29 | 30 | ## Features 31 | 32 | Creating a New Label Node: 33 | 34 | ``` 35 | - init(fontNamed: String?) 36 | Initializes a new label object with a specified font. 37 | ``` 38 | 39 | ``` 40 | - init(text: String?) 41 | Initializes a new label object with a text string. 42 | ``` 43 | 44 | 45 | - [x] ```lineSpacingFactor``` (to adjust space between letters) 46 | - [x] sequentially bouncing zoom animation 47 | - [x] shake 48 | 49 | ## ToDo 50 | - [x] new animations 51 | - [x] color blend factor like ```SKLabelNode``` 52 | - [x] blend mode 53 | - [x] ```init(attributedText: NSAttributedString?)``` 54 | 55 | ## Requirements 56 | 57 | - iOS 8.0+ 58 | - Xcode 9.2+ 59 | - Swift 4.0+ 60 | 61 | ## Communication 62 | 63 | - If you **found a bug**, open an issue. 64 | - If you **have a feature request**, open an issue. 65 | - If you **want to contribute**, submit a pull request. 66 | 67 | ## Installation 68 | 69 | Add the source file ```SKAdvancedLabelNode.swift``` to your project and use it. 70 | 71 | ## Usage 72 | 73 | ``` 74 | // horizontal alignment : left 75 | var advLabel = SKAdvancedLabelNode(fontNamed:"Optima-ExtraBlack") 76 | advLabel.name = "advLabel" 77 | advLabel.text = "labelTxt" 78 | advLabel.fontSize = 20.0 79 | advLabel.fontColor = .green 80 | advLabel.horizontalAlignmentMode = .left 81 | addChild(self.advLabel) 82 | advLabel.position = CGPoint(x:frame.width / 2.5, y:frame.height*0.70) 83 | advLabel.sequentiallyBouncingZoom(delay: 0.3,infinite: true) 84 | ``` 85 | 86 | ## License 87 | SKAdvancedLabelNode is released under the [MIT License](LICENSE) 88 | -------------------------------------------------------------------------------- /SKAdvancedLabelNode/GameScene.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GameScene.swift 3 | // SKAdvancedLabelNode 4 | // 5 | // Created by Alessandro Ornano on 14/01/2018. 6 | // Copyright © 2018 Alessandro Ornano. All rights reserved. 7 | // 8 | 9 | import SpriteKit 10 | 11 | class GameScene: SKScene { 12 | var label : SKLabelNode! 13 | var label2 : SKLabelNode! 14 | var label3 : SKLabelNode! 15 | var advLabel : SKAdvancedLabelNode! 16 | var advLabel2 : SKAdvancedLabelNode! 17 | var advLabel3 : SKAdvancedLabelNode! 18 | 19 | override func didMove(to view: SKView) { 20 | // #MARK: Classic labels 21 | // horizontal alignment : left 22 | let labelTxt = "Left Label Demo" 23 | self.label = SKLabelNode(fontNamed:"Optima-ExtraBlack") 24 | self.label.name = "label" 25 | self.label.text = labelTxt 26 | self.label.fontSize = 20.0 27 | self.label.fontColor = .white 28 | self.label.horizontalAlignmentMode = .left 29 | self.addChild(self.label) 30 | self.label.zPosition = 1 31 | self.label.position = CGPoint(x:frame.width / 2.5, y:frame.height*0.80) 32 | self.label.alpha = 1 33 | 34 | // horizontal alignment : center 35 | let label2Txt = "Center Label Demo" 36 | self.label2 = SKLabelNode(fontNamed:"Optima-ExtraBlack") 37 | self.label2.name = "label2" 38 | self.label2.text = label2Txt 39 | self.label2.fontSize = 20.0 40 | self.label2.fontColor = .white 41 | self.label2.horizontalAlignmentMode = .center 42 | self.addChild(self.label2) 43 | self.label2.position = CGPoint(x:frame.width / 2.5, y:frame.height*0.60) 44 | 45 | // horizontal alignment : right 46 | let label3Txt = "Right Label Demo" 47 | self.label3 = SKLabelNode(fontNamed:"Optima-ExtraBlack") 48 | self.label3.name = "label3" 49 | self.label3.text = label3Txt 50 | self.label3.fontSize = 20.0 51 | self.label3.fontColor = .white 52 | self.label3.horizontalAlignmentMode = .right 53 | self.addChild(self.label3) 54 | self.label3.position = CGPoint(x:frame.width / 2.5, y:frame.height*0.40) 55 | 56 | // #MARK: Advenced labels 57 | // horizontal alignment : left 58 | self.advLabel = SKAdvancedLabelNode(fontNamed:"Optima-ExtraBlack") 59 | self.advLabel.name = "advLabel" 60 | self.advLabel.text = labelTxt 61 | self.advLabel.fontSize = 20.0 62 | self.advLabel.fontColor = .green 63 | self.advLabel.horizontalAlignmentMode = .left 64 | self.addChild(self.advLabel) 65 | self.advLabel.position = CGPoint(x:frame.width / 2.5, y:frame.height*0.70) 66 | //self.advLabel.sequentiallyBouncingZoom(delay: 0.3,infinite: true) 67 | self.advLabel.shake(delay:1.0, infinite:true) 68 | 69 | // horizontal alignment : center 70 | self.advLabel2 = SKAdvancedLabelNode(fontNamed:"Optima-ExtraBlack") 71 | self.advLabel2.name = "advLabel2" 72 | self.advLabel2.text = label2Txt 73 | self.advLabel2.fontSize = 20.0 74 | self.advLabel2.fontColor = .green 75 | self.advLabel2.horizontalAlignmentMode = .center 76 | self.addChild(self.advLabel2) 77 | self.advLabel2.position = CGPoint(x:frame.width / 2.5, y:frame.height*0.50) 78 | //self.advLabel2.sequentiallyBouncingZoom(delay: 0.3,infinite: true) 79 | self.advLabel2.shake(delay:1.0, infinite:true) 80 | 81 | // horizontal alignment : right 82 | self.advLabel3 = SKAdvancedLabelNode(fontNamed:"Optima-ExtraBlack") 83 | self.advLabel3.name = "advLabel3" 84 | self.advLabel3.text = label3Txt 85 | self.advLabel3.fontSize = 20.0 86 | self.advLabel3.fontColor = .green 87 | self.advLabel3.horizontalAlignmentMode = .right 88 | self.addChild(self.advLabel3) 89 | self.advLabel3.position = CGPoint(x:frame.width / 2.5, y:frame.height*0.30) 90 | //self.advLabel3.sequentiallyBouncingZoom(delay: 0.3,infinite: true) 91 | self.advLabel3.shake(delay:1.0, infinite:true) 92 | 93 | // Description to understand what happened to screen: 94 | let node1 = SKLabelNode(fontNamed:"Arial") 95 | node1.name = "node1" 96 | node1.text = "classic label with white color" 97 | node1.fontSize = 10.0 98 | node1.fontColor = .white 99 | node1.horizontalAlignmentMode = .left 100 | self.addChild(node1) 101 | node1.position = CGPoint(x:frame.width / 1.5, y:frame.height*0.15) 102 | let node2 = SKLabelNode(fontNamed:"Arial") 103 | node2.name = "node2" 104 | node2.text = "advanced label with green color" 105 | node2.fontSize = 10.0 106 | node2.fontColor = .green 107 | node2.horizontalAlignmentMode = .left 108 | self.addChild(node2) 109 | node2.position = CGPoint(x:frame.width / 1.5, y:frame.height*0.10) 110 | 111 | } 112 | 113 | } 114 | -------------------------------------------------------------------------------- /SKAdvancedLabelNode/Source/SKAdvancedLabelNode.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SKAdvancedLabelNode.swift 3 | // Sprite-kit 4 | // 5 | // Created by Alessandro Ornano on 12/01/2018. 6 | // Copyright © 2018 Alessandro Ornano. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import SpriteKit 11 | 12 | class SKAdvancedLabelNode: SKNode { 13 | let colt = SKLabelNode() 14 | var labels = [SKLabelNode]() 15 | var text = "" { 16 | didSet { 17 | refreshLabels() 18 | } 19 | } 20 | var fontName = "HelveticaNeue-UltraLight" { 21 | didSet { 22 | _ = labels.flatMap({ $0.fontName = fontName }) 23 | refreshLabels() 24 | } 25 | } 26 | var fontSize = CGFloat(30.0) { 27 | didSet { 28 | _ = labels.flatMap({ $0.fontSize = fontSize }) 29 | refreshLabels() 30 | } 31 | } 32 | var fontColor = UIColor.init(white: 1.0, alpha: 1.0) { 33 | didSet { 34 | _ = labels.flatMap({ $0.fontColor = fontColor }) 35 | refreshLabels() 36 | } 37 | } 38 | var horizontalAlignmentMode = SKLabelHorizontalAlignmentMode.init(rawValue: 0) { // left 39 | didSet { 40 | _ = labels.flatMap({ $0.horizontalAlignmentMode = horizontalAlignmentMode! }) 41 | refreshLabels() 42 | } 43 | } 44 | var verticalAlignmentMode = SKLabelVerticalAlignmentMode.init(rawValue: 0) { // center 45 | didSet { 46 | _ = labels.flatMap({ $0.verticalAlignmentMode = verticalAlignmentMode! }) 47 | refreshLabels() 48 | } 49 | } 50 | var lineSpacingFactor: CGFloat = -1.3 { 51 | didSet { 52 | refreshLabels() 53 | } 54 | } 55 | override init() { 56 | super.init() 57 | } 58 | convenience init(text: String) { 59 | self.init() 60 | self.text = text 61 | } 62 | convenience init(fontNamed fontName: String?) { 63 | self.init(text: "") 64 | if let f = fontName { 65 | self.fontName = f 66 | } 67 | } 68 | required init?(coder aDecoder: NSCoder) { 69 | fatalError("init(coder:) has not been implemented") 70 | } 71 | 72 | func refreshLabels() { 73 | _ = labels.flatMap({ $0.removeFromParent() }) 74 | labels.removeAll() 75 | if text.count > 0 { 76 | var newX: CGFloat = 0.0 77 | var gapX: CGFloat = 0.0 78 | let ghostSpace = SKLabelNode(text: ".") 79 | ghostSpace.fontName = fontName 80 | ghostSpace.fontSize = fontSize 81 | let ghostSpaceWidth = ghostSpace.frame.size.width 82 | var fullNodeWidth:CGFloat = 0.0 83 | for char in text { 84 | if String(char) != " " { 85 | let charLabelNode = SKLabelNode(text: String(char)) 86 | charLabelNode.fontName = fontName 87 | charLabelNode.fontSize = fontSize 88 | fullNodeWidth += charLabelNode.frame.size.width + lineSpacingFactor 89 | } else { 90 | fullNodeWidth += ghostSpaceWidth + lineSpacingFactor 91 | } 92 | } 93 | switch horizontalAlignmentMode { 94 | case .left? : gapX = 0.0 95 | case .center? : gapX = fullNodeWidth/2 96 | case .right? : gapX = fullNodeWidth 97 | default: break 98 | } 99 | var index: Int = 0 100 | for char in text { 101 | if String(char) != " " { 102 | let charLabelNode = SKLabelNode(text: String(char)) 103 | charLabelNode.fontName = fontName 104 | charLabelNode.fontSize = fontSize 105 | charLabelNode.fontColor = fontColor 106 | charLabelNode.horizontalAlignmentMode = .left 107 | charLabelNode.verticalAlignmentMode = verticalAlignmentMode! 108 | charLabelNode.position.x = newX - gapX 109 | charLabelNode.alpha = 1 110 | self.addChild(charLabelNode) 111 | labels.append(charLabelNode) 112 | newX += charLabelNode.frame.size.width + lineSpacingFactor 113 | } else { 114 | ghostSpace.horizontalAlignmentMode = .left 115 | ghostSpace.verticalAlignmentMode = verticalAlignmentMode! 116 | ghostSpace.position.x = newX - gapX 117 | labels.append(ghostSpace) 118 | newX += ghostSpaceWidth + lineSpacingFactor 119 | } 120 | index += 1 121 | } 122 | } 123 | } 124 | } 125 | extension SKAction { 126 | class func afterDelay(_ delay: TimeInterval, performAction action: SKAction) -> SKAction { 127 | return SKAction.sequence([SKAction.wait(forDuration: delay), action]) 128 | } 129 | class func afterDelay(_ delay: TimeInterval, runBlock block: @escaping () -> Void) -> SKAction { 130 | return SKAction.afterDelay(delay, performAction: SKAction.run(block)) 131 | } 132 | } 133 | extension SKAction { 134 | class func shake(duration:TimeInterval, amplitudeX:CGFloat = 3.0, amplitudeY:CGFloat = 3, speed : TimeInterval = 0.015) -> SKAction { 135 | let numberOfShakes = duration / speed / 2.0 136 | var actionsArray:[SKAction] = [] 137 | for _ in 1...Int(numberOfShakes) { 138 | let dx = CGFloat(arc4random_uniform(UInt32(amplitudeX))) - CGFloat(amplitudeX / 2) 139 | let dy = CGFloat(arc4random_uniform(UInt32(amplitudeY))) - CGFloat(amplitudeY / 2) 140 | let forward = SKAction.moveBy(x: dx, y:dy, duration: speed) 141 | let reverse = forward.reversed() 142 | actionsArray.append(forward) 143 | actionsArray.append(reverse) 144 | } 145 | return SKAction.sequence(actionsArray) 146 | } 147 | } 148 | extension SKAdvancedLabelNode { 149 | // #-#-#-#-#-#-#-#-#-#-#-#-#-#-# 150 | //MARK: - Animations 151 | // #-#-#-#-#-#-#-#-#-#-#-#-#-#-# 152 | 153 | func sequentiallyBouncingZoom(delay:TimeInterval, infinite:Bool = false) { 154 | if labels.count > 0 && self.action(forKey: "sequentiallyBouncingZoom") == nil { 155 | let main = SKAction.run { [weak self] in 156 | guard let strongSelf = self else { return } 157 | for i in 0.. 0 && self.action(forKey: "shake") == nil { 176 | let originalPositions = self.labels.map({ return $0.position }) 177 | let main = SKAction.run { [weak self] in 178 | guard let strongSelf = self else { return } 179 | for i in 0..