├── 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 | [](https://developer.apple.com/swift)
6 | [](https://travis-ci.org/Alamofire/Alamofire)
7 | [](https://developer.apple.com/iphone/index.action)
9 | [](/LICENSE)
10 |
11 | Sequentially bouncing zoom animation:
12 |
13 | 
14 |
15 | Shake:
16 |
17 | 
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..