├── .github └── FUNDING.yml ├── .gitignore ├── .swiftlint.yml ├── .travis.yml ├── LICENSE ├── LTMorphingLabel.podspec ├── LTMorphingLabel ├── Info.plist ├── LTCharacterDiffResult.swift ├── LTCharacterLimbo.swift ├── LTEasing.swift ├── LTEmitterView.swift ├── LTMorphingEffect.swift ├── LTMorphingLabel+Anvil.swift ├── LTMorphingLabel+Burn.swift ├── LTMorphingLabel+Evaporate.swift ├── LTMorphingLabel+Fall.swift ├── LTMorphingLabel+Pixelate.swift ├── LTMorphingLabel+Sparkle.swift ├── LTMorphingLabel.h ├── LTMorphingLabel.swift ├── LTStringDiffResult.swift ├── Particles │ ├── Fire.png │ ├── Fragment.png │ ├── Smoke.png │ └── Sparkle.png ├── SwiftUI │ └── MorphingText.swift └── tvOS-Info.plist ├── LTMorphingLabelDemo.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── LTMorphingLabelDemo.xccheckout │ └── xcuserdata │ │ └── lex.xcuserdatad │ │ └── UserInterfaceState.xcuserstate ├── xcshareddata │ └── xcschemes │ │ ├── LTMorphingLabelDemo.xcscheme │ │ └── MorphingLabel.xcscheme └── xcuserdata │ └── lex.xcuserdatad │ └── xcschemes │ └── xcschememanagement.plist ├── LTMorphingLabelDemo ├── AppDelegate.swift ├── Images.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── Icon-60@2x.png │ │ ├── Icon-60@3x.png │ │ ├── Icon-76.png │ │ ├── Icon-76@2x.png │ │ └── Icon-83.5@2x.png │ └── Contents.json ├── Info.plist ├── LTDemoViewController.swift ├── LTMorphingLabelDemo.storyboard └── Launch Screen.storyboard ├── LTMorphingLabelTests ├── Info.plist └── LTMorphingLabelTests.swift ├── LTMorphingLabelUITests ├── Info.plist └── LTMorphingLabelUITests.swift ├── Package.swift ├── README.md ├── Sources └── MorphingLabel └── build_xcframework.sh /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: ltmorphinglabel 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/osx,carthage,swift 3 | 4 | ### Carthage ### 5 | # Carthage 6 | # 7 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 8 | # Carthage/Checkouts 9 | 10 | Carthage/Build 11 | 12 | ### OSX ### 13 | *.DS_Store 14 | .AppleDouble 15 | .LSOverride 16 | 17 | # Icon must end with two \r 18 | Icon 19 | 20 | # Thumbnails 21 | ._* 22 | 23 | # Files that might appear in the root of a volume 24 | .DocumentRevisions-V100 25 | .fseventsd 26 | .Spotlight-V100 27 | .TemporaryItems 28 | .Trashes 29 | .VolumeIcon.icns 30 | .com.apple.timemachine.donotpresent 31 | 32 | # Directories potentially created on remote AFP share 33 | .AppleDB 34 | .AppleDesktop 35 | Network Trash Folder 36 | Temporary Items 37 | .apdisk 38 | 39 | ### Swift ### 40 | # Xcode 41 | # 42 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 43 | 44 | ## Build generated 45 | build/ 46 | DerivedData/ 47 | 48 | ## Various settings 49 | *.pbxuser 50 | !default.pbxuser 51 | *.mode1v3 52 | !default.mode1v3 53 | *.mode2v3 54 | !default.mode2v3 55 | *.perspectivev3 56 | !default.perspectivev3 57 | xcuserdata/ 58 | 59 | ## Other 60 | *.moved-aside 61 | *.xccheckout 62 | *.xcscmblueprint 63 | 64 | ## Obj-C/Swift specific 65 | *.hmap 66 | *.ipa 67 | *.dSYM.zip 68 | *.dSYM 69 | 70 | ## Playgrounds 71 | timeline.xctimeline 72 | playground.xcworkspace 73 | 74 | # Swift Package Manager 75 | # 76 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 77 | # Packages/ 78 | # Package.pins 79 | .build/ 80 | 81 | # CocoaPods - Refactored to standalone file 82 | 83 | # Carthage - Refactored to standalone file 84 | 85 | # fastlane 86 | # 87 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 88 | # screenshots whenever they are needed. 89 | # For more information about the recommended setup visit: 90 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 91 | 92 | fastlane/report.xml 93 | fastlane/Preview.html 94 | fastlane/screenshots 95 | fastlane/test_output 96 | 97 | # End of https://www.gitignore.io/api/osx,carthage,swift 98 | -------------------------------------------------------------------------------- /.swiftlint.yml: -------------------------------------------------------------------------------- 1 | disabled_rules: 2 | - todo 3 | - force_cast 4 | - trailing_whitespace 5 | - colon 6 | - identifier_name 7 | - type_name 8 | - comma 9 | - closure_parameter_position 10 | excluded: 11 | - Carthage 12 | - Pods 13 | line_length: 120 14 | function_body_length: 15 | - 230 16 | - 300 17 | type_body_length: 18 | - 300 19 | - 600 20 | file_length: 21 | - 600 22 | - 800 23 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | osx_image: xcode12 2 | language: objective-c 3 | script: 4 | - | 5 | xcodebuild test \ 6 | -project LTMorphingLabelDemo.xcodeproj \ 7 | -scheme LTMorphingLabelDemo \ 8 | -sdk iphonesimulator \ 9 | -destination 'platform=iOS Simulator,name=iPhone X,OS=latest' \ 10 | | xcpretty 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright © 2020 Lex Tang, https://lexrus.com 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the “Software”), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /LTMorphingLabel.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | 3 | s.name = "LTMorphingLabel" 4 | s.version = "0.9.3" 5 | s.summary = "Graceful morphing effects for UILabel written in Swift." 6 | s.description = <<-DESC 7 | A morphing UILabel subclass written in Swift. 8 | The .Scale effect mimicked Apple's QuickType animation of iOS 9 | 8 in WWDC 2014. New morphing effects are available as 10 | Swift extensions. 11 | DESC 12 | s.homepage = "https://github.com/lexrus/LTMorphingLabel" 13 | s.screenshots = "https://cloud.githubusercontent.com/assets/219689/3491822/96bf5de6-059d-11e4-9826-a6f82025d1af.gif", 14 | "https://cloud.githubusercontent.com/assets/219689/3491838/ffc5aff2-059d-11e4-970c-6e2d7664785a.gif", 15 | "https://cloud.githubusercontent.com/assets/219689/3491840/173c2238-059e-11e4-9b33-dcd21edae9e2.gif", 16 | "https://cloud.githubusercontent.com/assets/219689/3491845/29bb0f8c-059e-11e4-9ef8-de56bec1baba.gif", 17 | "https://cloud.githubusercontent.com/assets/219689/3508789/31e9fafe-0690-11e4-9a76-ba3ef45eb53a.gif", 18 | "https://cloud.githubusercontent.com/assets/219689/3594949/815cd3e8-0caa-11e4-9738-278a9c959478.gif" 19 | s.license = { :type => "MIT", :file => "LICENSE" } 20 | s.author = { "Lex Tang" => "lexrus@gmail.com" } 21 | s.social_media_url = "https://twitter.com/lexrus" 22 | s.ios.deployment_target = "9.0" 23 | s.tvos.deployment_target = "9.0" 24 | s.swift_versions = ['5.3'] 25 | s.source = { 26 | :git => "https://github.com/lexrus/LTMorphingLabel.git", 27 | :tag => s.version 28 | } 29 | s.source_files = "LTMorphingLabel/*.{h,swift}", "LTMorphingLabel/**/*.{swift}" 30 | s.resources = "LTMorphingLabel/Particles/*.png" 31 | s.frameworks = "UIKit", "Foundation", "QuartzCore" 32 | s.requires_arc = true 33 | 34 | end 35 | -------------------------------------------------------------------------------- /LTMorphingLabel/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /LTMorphingLabel/LTCharacterDiffResult.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LTCharacterDiffResult.swift 3 | // https://github.com/lexrus/LTMorphingLabel 4 | // 5 | // The MIT License (MIT) 6 | // Copyright (c) 2017 Lex Tang, http://lexrus.com 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a 9 | // copy of this software and associated documentation files 10 | // (the “Software”), to deal in the Software without restriction, 11 | // including without limitation the rights to use, copy, modify, merge, 12 | // publish, distribute, sublicense, and/or sell copies of the Software, 13 | // and to permit persons to whom the Software is furnished to do so, 14 | // subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included 17 | // in all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS 20 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 22 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 23 | // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 24 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 25 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | // 27 | 28 | import Foundation 29 | 30 | public enum LTCharacterDiffResult: CustomDebugStringConvertible, Equatable { 31 | 32 | case same 33 | case add 34 | case delete 35 | case move(offset: Int) 36 | case moveAndAdd(offset: Int) 37 | case replace 38 | 39 | public var debugDescription: String { 40 | switch self { 41 | case .same: 42 | return "The character is unchanged." 43 | case .add: 44 | return "A new character is ADDED." 45 | case .delete: 46 | return "The character is DELETED." 47 | case .move(let offset): 48 | return "The character is MOVED to \(offset)." 49 | case .moveAndAdd(let offset): 50 | return "The character is MOVED to \(offset) and a new character is ADDED." 51 | case .replace: 52 | return "The character is REPLACED with a new character." 53 | } 54 | } 55 | 56 | } 57 | 58 | public func == (lhs: LTCharacterDiffResult, rhs: LTCharacterDiffResult) -> Bool { 59 | switch (lhs, rhs) { 60 | case (.move(let offset0), .move(let offset1)): 61 | return offset0 == offset1 62 | 63 | case (.moveAndAdd(let offset0), .moveAndAdd(let offset1)): 64 | return offset0 == offset1 65 | 66 | case (.add, .add): 67 | return true 68 | 69 | case (.delete, .delete): 70 | return true 71 | 72 | case (.replace, .replace): 73 | return true 74 | 75 | case (.same, .same): 76 | return true 77 | 78 | default: return false 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /LTMorphingLabel/LTCharacterLimbo.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LTCharacterLimbo.swift 3 | // https://github.com/lexrus/LTMorphingLabel 4 | // 5 | // The MIT License (MIT) 6 | // Copyright (c) 2017 Lex Tang, http://lexrus.com 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a 9 | // copy of this software and associated documentation files 10 | // (the “Software”), to deal in the Software without restriction, 11 | // including without limitation the rights to use, copy, modify, merge, 12 | // publish, distribute, sublicense, and/or sell copies of the Software, 13 | // and to permit persons to whom the Software is furnished to do so, 14 | // subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included 17 | // in all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS 20 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 22 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 23 | // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 24 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 25 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | // 27 | 28 | import UIKit 29 | 30 | public struct LTCharacterLimbo: CustomDebugStringConvertible { 31 | 32 | public let char: Character 33 | public var rect: CGRect 34 | public var alpha: CGFloat 35 | public var size: CGFloat 36 | public var drawingProgress: CGFloat = 0.0 37 | 38 | public var debugDescription: String { 39 | return "Character: '\(char)'" 40 | + "drawIn (\(rect.origin.x), \(rect.origin.y), " 41 | + "\(rect.size.width)x\(rect.size.height) " 42 | + "with alpha \(alpha) " 43 | + "and \(size)pt font." 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /LTMorphingLabel/LTEasing.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LTEasing.swift 3 | // LTMorphingLabelDemo 4 | // 5 | // Created by Lex on 7/1/14. 6 | // Copyright (c) 2015 lexrus.com. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | // http://gsgd.co.uk/sandbox/jquery/easing/jquery.easing.1.3.js 12 | // t = currentTime 13 | // b = beginning 14 | // c = change 15 | // d = duration 16 | 17 | public struct LTEasing { 18 | 19 | public static func easeOutQuint(_ t: Float, _ b: Float, _ c: Float, _ d: Float = 1.0) -> Float { 20 | return { (f: Float) in 21 | return c * (pow(f, 5) + 1.0) + b 22 | }(t / d - 1.0) 23 | } 24 | 25 | public static func easeInQuint(_ t: Float, _ b: Float, _ c: Float, _ d: Float = 1.0) -> Float { 26 | return { (f: Float) in 27 | c * pow(f, 5) + b 28 | }(t / d) 29 | } 30 | 31 | public static func easeOutBack(_ t: Float, _ b: Float, _ c: Float, _ d: Float = 1.0) -> Float { 32 | let s: Float = 2.70158 33 | let t2: Float = t / d - 1.0 34 | return Float(c * (t2 * t2 * ((s + 1.0) * t2 + s) + 1.0)) + b 35 | } 36 | 37 | public static func easeOutBounce(_ t: Float, _ b: Float, _ c: Float, _ d: Float = 1.0) -> Float { 38 | return { (f: Float) in 39 | if f < 1 / 2.75 { 40 | return c * 7.5625 * f * f + b 41 | } else if f < 2 / 2.75 { 42 | let t = f - 1.5 / 2.75 43 | return c * (7.5625 * t * t + 0.75) + b 44 | } else if f < 2.5 / 2.75 { 45 | let t = f - 2.25 / 2.75 46 | return c * (7.5625 * t * t + 0.9375) + b 47 | } else { 48 | let t = f - 2.625 / 2.75 49 | return c * (7.5625 * t * t + 0.984375) + b 50 | } 51 | }(t / d) 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /LTMorphingLabel/LTEmitterView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LTEmitterView.swift 3 | // https://github.com/lexrus/LTMorphingLabel 4 | // 5 | // The MIT License (MIT) 6 | // Copyright (c) 2017 Lex Tang, http://lexrus.com 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a 9 | // copy of this software and associated documentation files 10 | // (the “Software”), to deal in the Software without restriction, 11 | // including without limitation the rights to use, copy, modify, merge, 12 | // publish, distribute, sublicense, and/or sell copies of the Software, 13 | // and to permit persons to whom the Software is furnished to do so, 14 | // subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included 17 | // in all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS 20 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 22 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 23 | // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 24 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 25 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | // 27 | 28 | import UIKit 29 | 30 | private func < (lhs: T?, rhs: T?) -> Bool { 31 | switch (lhs, rhs) { 32 | case let (l?, r?): 33 | return l < r 34 | case (nil, _?): 35 | return true 36 | default: 37 | return false 38 | } 39 | } 40 | 41 | private func > (lhs: T?, rhs: T?) -> Bool { 42 | switch (lhs, rhs) { 43 | case let (l?, r?): 44 | return l > r 45 | default: 46 | return rhs < lhs 47 | } 48 | } 49 | 50 | public struct LTEmitter { 51 | 52 | let layer: CAEmitterLayer = { 53 | let layer = CAEmitterLayer() 54 | layer.emitterPosition = CGPoint(x: 10, y: 10) 55 | layer.emitterSize = CGSize(width: 10, height: 1) 56 | layer.renderMode = .unordered 57 | layer.emitterShape = .line 58 | return layer 59 | }() 60 | 61 | let cell: CAEmitterCell = { 62 | let cell = CAEmitterCell() 63 | cell.name = "sparkle" 64 | cell.birthRate = 150.0 65 | cell.velocity = 50.0 66 | cell.velocityRange = -80.0 67 | cell.lifetime = 0.16 68 | cell.lifetimeRange = 0.1 69 | cell.emissionLongitude = CGFloat(Double.pi / 2 * 2.0) 70 | cell.emissionRange = CGFloat(Double.pi / 2 * 2.0) 71 | cell.scale = 0.1 72 | cell.yAcceleration = 100 73 | cell.scaleSpeed = -0.06 74 | cell.scaleRange = 0.1 75 | return cell 76 | }() 77 | 78 | public var duration: Float = 0.6 79 | 80 | init(name: String, particleName: String, duration: Float) { 81 | cell.name = name 82 | self.duration = duration 83 | var image: UIImage? 84 | defer { 85 | cell.contents = image?.cgImage 86 | } 87 | 88 | image = UIImage(named: particleName) 89 | 90 | if image != nil { 91 | return 92 | } 93 | // Load from Framework 94 | image = UIImage( 95 | named: particleName, 96 | in: Bundle(for: LTMorphingLabel.self), 97 | compatibleWith: nil) 98 | } 99 | 100 | public func play() { 101 | if layer.emitterCells?.count > 0 { 102 | return 103 | } 104 | 105 | layer.emitterCells = [cell] 106 | let d = DispatchTime.now() + Double(Int64(duration * Float(NSEC_PER_SEC))) / Double(NSEC_PER_SEC) 107 | DispatchQueue.main.asyncAfter(deadline: d) { [weak layer] in 108 | layer?.birthRate = 0.0 109 | } 110 | } 111 | 112 | public func stop() { 113 | if nil != layer.superlayer { 114 | layer.emitterCells = nil 115 | layer.removeFromSuperlayer() 116 | } 117 | } 118 | 119 | func update(_ configureClosure: LTEmitterConfigureClosure? = .none) -> LTEmitter { 120 | configureClosure?(layer, cell) 121 | return self 122 | } 123 | 124 | } 125 | 126 | public typealias LTEmitterConfigureClosure = (CAEmitterLayer, CAEmitterCell) -> Void 127 | 128 | open class LTEmitterView: UIView { 129 | 130 | open lazy var emitters: [String: LTEmitter] = { 131 | var _emitters = [String: LTEmitter]() 132 | return _emitters 133 | }() 134 | 135 | open func createEmitter( 136 | _ name: String, 137 | particleName: String, 138 | duration: Float, 139 | configureClosure: LTEmitterConfigureClosure? 140 | ) -> LTEmitter { 141 | 142 | var emitter: LTEmitter 143 | if let e = emitters[name] { 144 | emitter = e 145 | } else { 146 | emitter = LTEmitter( 147 | name: name, 148 | particleName: particleName, 149 | duration: duration 150 | ) 151 | 152 | configureClosure?(emitter.layer, emitter.cell) 153 | 154 | layer.addSublayer(emitter.layer) 155 | emitters.updateValue(emitter, forKey: name) 156 | } 157 | return emitter 158 | } 159 | 160 | open func removeAllEmitters() { 161 | emitters.forEach { 162 | $0.value.layer.removeFromSuperlayer() 163 | } 164 | emitters.removeAll(keepingCapacity: false) 165 | } 166 | 167 | } 168 | -------------------------------------------------------------------------------- /LTMorphingLabel/LTMorphingEffect.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LTMorphingEffect.swift 3 | // https://github.com/lexrus/LTMorphingLabel 4 | // 5 | // The MIT License (MIT) 6 | // Copyright (c) 2017 Lex Tang, http://lexrus.com 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a 9 | // copy of this software and associated documentation files 10 | // (the “Software”), to deal in the Software without restriction, 11 | // including without limitation the rights to use, copy, modify, merge, 12 | // publish, distribute, sublicense, and/or sell copies of the Software, 13 | // and to permit persons to whom the Software is furnished to do so, 14 | // subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included 17 | // in all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS 20 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 22 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 23 | // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 24 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 25 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | // 27 | 28 | import UIKit 29 | 30 | @objc public enum LTMorphingEffect 31 | : Int 32 | , CustomStringConvertible 33 | , ExpressibleByIntegerLiteral 34 | , ExpressibleByStringLiteral 35 | , CaseIterable { 36 | 37 | public typealias IntegerLiteralType = Int 38 | public typealias StringLiteralType = String 39 | 40 | case scale = 0 41 | case evaporate 42 | case fall 43 | case pixelate 44 | case sparkle 45 | case burn 46 | case anvil 47 | 48 | public static let allValues = [ 49 | "Scale", "Evaporate", "Fall", "Pixelate", "Sparkle", "Burn", "Anvil" 50 | ] 51 | 52 | public var description: String { 53 | switch self { 54 | case .evaporate: 55 | return "Evaporate" 56 | case .fall: 57 | return "Fall" 58 | case .pixelate: 59 | return "Pixelate" 60 | case .sparkle: 61 | return "Sparkle" 62 | case .burn: 63 | return "Burn" 64 | case .anvil: 65 | return "Anvil" 66 | default: 67 | return "Scale" 68 | } 69 | } 70 | 71 | public init(integerLiteral value: Int) { 72 | self = LTMorphingEffect(rawValue: value) ?? .scale 73 | } 74 | 75 | public init(stringLiteral value: String) { 76 | self = { 77 | switch value { 78 | case "Evaporate": return .evaporate 79 | case "Fall": return .fall 80 | case "Pixelate": return .pixelate 81 | case "Sparkle": return .sparkle 82 | case "Burn": return .burn 83 | case "Anvil": return .anvil 84 | default: return .scale 85 | } 86 | }() 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /LTMorphingLabel/LTMorphingLabel+Anvil.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LTMorphingLabel+Anvil.swift 3 | // https://github.com/lexrus/LTMorphingLabel 4 | // 5 | // The MIT License (MIT) 6 | // Copyright (c) 2017 Lex Tang, http://lexrus.com 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a 9 | // copy of this software and associated documentation files 10 | // (the “Software”), to deal in the Software without restriction, 11 | // including without limitation the rights to use, copy, modify, merge, 12 | // publish, distribute, sublicense, and/or sell copies of the Software, 13 | // and to permit persons to whom the Software is furnished to do so, 14 | // subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included 17 | // in all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS 20 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 22 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 23 | // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 24 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 25 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | // 27 | 28 | import UIKit 29 | 30 | extension LTMorphingLabel { 31 | 32 | @objc 33 | func AnvilLoad() { 34 | 35 | startClosures["Anvil\(LTMorphingPhases.start)"] = { 36 | self.emitterView.removeAllEmitters() 37 | 38 | guard self.newRects.count > 0 else { return } 39 | 40 | let centerRect = self.newRects[Int(self.newRects.count / 2)] 41 | 42 | _ = self.emitterView.createEmitter( 43 | "leftSmoke", 44 | particleName: "Smoke", 45 | duration: 0.6 46 | ) { (layer, cell) in 47 | layer.emitterSize = CGSize(width: 1, height: 1) 48 | layer.emitterPosition = CGPoint( 49 | x: centerRect.origin.x, 50 | y: centerRect.origin.y + centerRect.size.height / 1.3) 51 | layer.renderMode = .unordered 52 | cell.emissionLongitude = CGFloat(Double.pi / 2) 53 | cell.scale = self.font.pointSize / 90.0 54 | cell.scaleSpeed = self.font.pointSize / 130 55 | cell.birthRate = 60 56 | cell.velocity = CGFloat(80 + Int(arc4random_uniform(60))) 57 | cell.velocityRange = 100 58 | cell.yAcceleration = -40 59 | cell.xAcceleration = 70 60 | cell.emissionLongitude = CGFloat(-Double.pi / 2) 61 | cell.emissionRange = CGFloat(Double.pi / 4) / 5.0 62 | cell.lifetime = self.morphingDuration * 2.0 63 | cell.spin = 10 64 | cell.alphaSpeed = -0.5 / self.morphingDuration 65 | } 66 | 67 | _ = self.emitterView.createEmitter( 68 | "rightSmoke", 69 | particleName: "Smoke", 70 | duration: 0.6 71 | ) { (layer, cell) in 72 | layer.emitterSize = CGSize(width: 1, height: 1) 73 | layer.emitterPosition = CGPoint( 74 | x: centerRect.origin.x, 75 | y: centerRect.origin.y + centerRect.size.height / 1.3) 76 | layer.renderMode = .unordered 77 | cell.emissionLongitude = CGFloat(Double.pi / 2) 78 | cell.scale = self.font.pointSize / 90.0 79 | cell.scaleSpeed = self.font.pointSize / 130 80 | cell.birthRate = 60 81 | cell.velocity = CGFloat(80 + Int(arc4random_uniform(60))) 82 | cell.velocityRange = 100 83 | cell.yAcceleration = -40 84 | cell.xAcceleration = -70 85 | cell.emissionLongitude = CGFloat(Double.pi / 2) 86 | cell.emissionRange = CGFloat(-Double.pi / 4) / 5.0 87 | cell.lifetime = self.morphingDuration * 2.0 88 | cell.spin = -10 89 | cell.alphaSpeed = -0.5 / self.morphingDuration 90 | } 91 | 92 | _ = self.emitterView.createEmitter( 93 | "leftFragments", 94 | particleName: "Fragment", 95 | duration: 0.6 96 | ) { (layer, cell) in 97 | layer.emitterSize = CGSize( 98 | width: self.font.pointSize, 99 | height: 1 100 | ) 101 | layer.emitterPosition = CGPoint( 102 | x: centerRect.origin.x, 103 | y: centerRect.origin.y + centerRect.size.height / 1.3 104 | ) 105 | cell.scale = self.font.pointSize / 90.0 106 | cell.scaleSpeed = self.font.pointSize / 40.0 107 | cell.color = self.textColor.cgColor 108 | cell.birthRate = 60 109 | cell.velocity = 350 110 | cell.yAcceleration = 0 111 | cell.xAcceleration = CGFloat(10 * Int(arc4random_uniform(10))) 112 | cell.emissionLongitude = CGFloat(-Double.pi / 2) 113 | cell.emissionRange = CGFloat(Double.pi / 4) / 5.0 114 | cell.alphaSpeed = -2 115 | cell.lifetime = self.morphingDuration 116 | } 117 | 118 | _ = self.emitterView.createEmitter( 119 | "rightFragments", 120 | particleName: "Fragment", 121 | duration: 0.6 122 | ) { (layer, cell) in 123 | layer.emitterSize = CGSize( 124 | width: self.font.pointSize, 125 | height: 1 126 | ) 127 | layer.emitterPosition = CGPoint( 128 | x: centerRect.origin.x, 129 | y: centerRect.origin.y + centerRect.size.height / 1.3) 130 | cell.scale = self.font.pointSize / 90.0 131 | cell.scaleSpeed = self.font.pointSize / 40.0 132 | cell.color = self.textColor.cgColor 133 | cell.birthRate = 60 134 | cell.velocity = 350 135 | cell.yAcceleration = 0 136 | cell.xAcceleration = CGFloat(-10 * Int(arc4random_uniform(10))) 137 | cell.emissionLongitude = CGFloat(Double.pi / 2) 138 | cell.emissionRange = CGFloat(-Double.pi / 4) / 5.0 139 | cell.alphaSpeed = -2 140 | cell.lifetime = self.morphingDuration 141 | } 142 | 143 | _ = self.emitterView.createEmitter( 144 | "fragments", 145 | particleName: "Fragment", 146 | duration: 0.6 147 | ) { (layer, cell) in 148 | layer.emitterSize = CGSize( 149 | width: self.font.pointSize, 150 | height: 1 151 | ) 152 | layer.emitterPosition = CGPoint( 153 | x: centerRect.origin.x, 154 | y: centerRect.origin.y + centerRect.size.height / 1.3) 155 | cell.scale = self.font.pointSize / 90.0 156 | cell.scaleSpeed = self.font.pointSize / 40.0 157 | cell.color = self.textColor.cgColor 158 | cell.birthRate = 60 159 | cell.velocity = 250 160 | cell.velocityRange = CGFloat(Int(arc4random_uniform(20)) + 30) 161 | cell.yAcceleration = 500 162 | cell.emissionLongitude = 0 163 | cell.emissionRange = CGFloat(Double.pi / 2) 164 | cell.alphaSpeed = -1 165 | cell.lifetime = self.morphingDuration 166 | } 167 | } 168 | 169 | progressClosures["Anvil\(LTMorphingPhases.progress)"] = { 170 | (index: Int, progress: Float, isNewChar: Bool) in 171 | 172 | if !isNewChar { 173 | return min(1.0, max(0.0, progress)) 174 | } 175 | 176 | let j = Float(sin(Float(index))) * 1.7 177 | return min(1.0, max(0.0001, progress + self.morphingCharacterDelay * j)) 178 | 179 | } 180 | 181 | effectClosures["Anvil\(LTMorphingPhases.disappear)"] = { 182 | char, index, progress in 183 | 184 | return LTCharacterLimbo( 185 | char: char, 186 | rect: self.previousRects[index], 187 | alpha: CGFloat(1.0 - progress), 188 | size: self.font.pointSize, 189 | drawingProgress: 0.0) 190 | } 191 | 192 | effectClosures["Anvil\(LTMorphingPhases.appear)"] = { 193 | char, index, progress in 194 | 195 | var rect = self.newRects[index] 196 | 197 | if progress < 1.0 { 198 | let easingValue = LTEasing.easeOutBounce(progress, 0.0, 1.0) 199 | rect.origin.y = CGFloat(Float(rect.origin.y) * easingValue) 200 | } 201 | 202 | if progress > self.morphingDuration * 0.5 { 203 | let end = self.morphingDuration * 0.55 204 | self.emitterView.createEmitter( 205 | "fragments", 206 | particleName: "Fragment", 207 | duration: 0.6 208 | ) { (_, _) in }.update { (layer, _) in 209 | if progress > end { 210 | layer.birthRate = 0 211 | } 212 | }.play() 213 | self.emitterView.createEmitter( 214 | "leftFragments", 215 | particleName: "Fragment", 216 | duration: 0.6 217 | ) { (_, _) in }.update { (layer, _) in 218 | if progress > end { 219 | layer.birthRate = 0 220 | } 221 | }.play() 222 | self.emitterView.createEmitter( 223 | "rightFragments", 224 | particleName: "Fragment", 225 | duration: 0.6 226 | ) { (_, _) in }.update { (layer, _) in 227 | if progress > end { 228 | layer.birthRate = 0 229 | } 230 | }.play() 231 | } 232 | 233 | if progress > self.morphingDuration * 0.63 { 234 | let end = self.morphingDuration * 0.7 235 | self.emitterView.createEmitter( 236 | "leftSmoke", 237 | particleName: "Smoke", 238 | duration: 0.6 239 | ) { (_, _) in }.update { (layer, _) in 240 | if progress > end { 241 | layer.birthRate = 0 242 | } 243 | }.play() 244 | self.emitterView.createEmitter( 245 | "rightSmoke", 246 | particleName: "Smoke", 247 | duration: 0.6 248 | ) { (_, _) in }.update { (layer, _) in 249 | if progress > end { 250 | layer.birthRate = 0 251 | } 252 | }.play() 253 | } 254 | 255 | return LTCharacterLimbo( 256 | char: char, 257 | rect: rect, 258 | alpha: CGFloat(self.morphingProgress), 259 | size: self.font.pointSize, 260 | drawingProgress: CGFloat(progress) 261 | ) 262 | } 263 | } 264 | 265 | } 266 | -------------------------------------------------------------------------------- /LTMorphingLabel/LTMorphingLabel+Burn.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LTMorphingLabel+Burn.swift 3 | // https://github.com/lexrus/LTMorphingLabel 4 | // 5 | // The MIT License (MIT) 6 | // Copyright (c) 2017 Lex Tang, http://lexrus.com 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a 9 | // copy of this software and associated documentation files 10 | // (the “Software”), to deal in the Software without restriction, 11 | // including without limitation the rights to use, copy, modify, merge, 12 | // publish, distribute, sublicense, and/or sell copies of the Software, 13 | // and to permit persons to whom the Software is furnished to do so, 14 | // subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included 17 | // in all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS 20 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 22 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 23 | // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 24 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 25 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | // 27 | 28 | import UIKit 29 | 30 | extension LTMorphingLabel { 31 | 32 | fileprivate func burningImageForCharLimbo( 33 | _ charLimbo: LTCharacterLimbo, 34 | withProgress progress: CGFloat 35 | ) -> (UIImage, CGRect) { 36 | let maskedHeight = charLimbo.rect.size.height * max(0.01, progress) 37 | let maskedSize = CGSize( 38 | width: charLimbo.rect.size.width, 39 | height: maskedHeight 40 | ) 41 | UIGraphicsBeginImageContextWithOptions( 42 | maskedSize, 43 | false, 44 | UIScreen.main.scale 45 | ) 46 | let rect = CGRect( 47 | x: 0, 48 | y: 0, 49 | width: charLimbo.rect.size.width, 50 | height: maskedHeight 51 | ) 52 | String(charLimbo.char).draw(in: rect, withAttributes: [ 53 | .font: self.font as Any, 54 | .foregroundColor: self.textColor as Any 55 | ]) 56 | guard let newImage = UIGraphicsGetImageFromCurrentImageContext() else { 57 | return (UIImage() , .zero) 58 | } 59 | 60 | UIGraphicsEndImageContext() 61 | let newRect = CGRect( 62 | x: charLimbo.rect.origin.x, 63 | y: charLimbo.rect.origin.y, 64 | width: charLimbo.rect.size.width, 65 | height: maskedHeight 66 | ) 67 | return (newImage, newRect) 68 | } 69 | 70 | @objc 71 | func BurnLoad() { 72 | 73 | startClosures["Burn\(LTMorphingPhases.start)"] = { 74 | self.emitterView.removeAllEmitters() 75 | } 76 | 77 | progressClosures["Burn\(LTMorphingPhases.progress)"] = { 78 | index, progress, isNewChar in 79 | 80 | if !isNewChar { 81 | return min(1.0, max(0.0, progress)) 82 | } 83 | 84 | let j = Float(sin(Float(index))) * 1.5 85 | return min(1.0, max(0.0001, progress + self.morphingCharacterDelay * j)) 86 | 87 | } 88 | 89 | effectClosures["Burn\(LTMorphingPhases.disappear)"] = { 90 | char, index, progress in 91 | 92 | return LTCharacterLimbo( 93 | char: char, 94 | rect: self.previousRects[index], 95 | alpha: CGFloat(1.0 - progress), 96 | size: self.font.pointSize, 97 | drawingProgress: 0.0 98 | ) 99 | } 100 | 101 | effectClosures["Burn\(LTMorphingPhases.appear)"] = { 102 | char, index, progress in 103 | 104 | if char != " " { 105 | let rect = self.newRects[index] 106 | let emitterPosition = CGPoint( 107 | x: rect.origin.x + rect.size.width / 2.0, 108 | y: CGFloat(progress) * rect.size.height / 1.2 + rect.origin.y 109 | ) 110 | 111 | self.emitterView.createEmitter( 112 | "c\(index)", 113 | particleName: "Fire", 114 | duration: self.morphingDuration 115 | ) { (layer, cell) in 116 | layer.emitterSize = CGSize( 117 | width: rect.size.width, 118 | height: 1 119 | ) 120 | layer.renderMode = CAEmitterLayerRenderMode.additive 121 | layer.emitterMode = CAEmitterLayerEmitterMode.outline 122 | cell.emissionLongitude = CGFloat(Double.pi / 2) 123 | cell.scale = self.font.pointSize / 160.0 124 | cell.scaleSpeed = self.font.pointSize / 100.0 125 | cell.birthRate = Float(self.font.pointSize) 126 | cell.emissionLongitude = CGFloat(arc4random_uniform(30)) 127 | cell.emissionRange = CGFloat(Double.pi / 4) 128 | cell.alphaSpeed = self.morphingDuration * -3.0 129 | cell.yAcceleration = 10 130 | cell.velocity = CGFloat(10 + Int(arc4random_uniform(3))) 131 | cell.velocityRange = 10 132 | cell.spin = 0 133 | cell.spinRange = 0 134 | cell.lifetime = self.morphingDuration / 3.0 135 | }.update { (layer, _) in 136 | layer.emitterPosition = emitterPosition 137 | }.play() 138 | 139 | self.emitterView.createEmitter( 140 | "s\(index)", 141 | particleName: "Smoke", 142 | duration: self.morphingDuration 143 | ) { (layer, cell) in 144 | layer.emitterSize = CGSize( 145 | width: rect.size.width, 146 | height: 10 147 | ) 148 | layer.renderMode = CAEmitterLayerRenderMode.additive 149 | layer.emitterMode = CAEmitterLayerEmitterMode.volume 150 | cell.emissionLongitude = CGFloat(Double.pi / 2) 151 | cell.scale = self.font.pointSize / 40.0 152 | cell.scaleSpeed = self.font.pointSize / 100.0 153 | cell.birthRate = 154 | Float(self.font.pointSize) 155 | / Float(arc4random_uniform(10) + 10) 156 | cell.emissionLongitude = 0 157 | cell.emissionRange = CGFloat(Double.pi / 4) 158 | cell.alphaSpeed = self.morphingDuration * -3 159 | cell.yAcceleration = -5 160 | cell.velocity = CGFloat(20 + Int(arc4random_uniform(15))) 161 | cell.velocityRange = 20 162 | cell.spin = CGFloat(Float(arc4random_uniform(30)) / 10.0) 163 | cell.spinRange = 3 164 | cell.lifetime = self.morphingDuration 165 | }.update { (layer, _) in 166 | layer.emitterPosition = emitterPosition 167 | }.play() 168 | } 169 | 170 | return LTCharacterLimbo( 171 | char: char, 172 | rect: self.newRects[index], 173 | alpha: 1.0, 174 | size: self.font.pointSize, 175 | drawingProgress: CGFloat(progress) 176 | ) 177 | } 178 | 179 | drawingClosures["Burn\(LTMorphingPhases.draw)"] = { 180 | (charLimbo: LTCharacterLimbo) in 181 | 182 | if charLimbo.drawingProgress > 0.0 { 183 | 184 | let (charImage, rect) = self.burningImageForCharLimbo( 185 | charLimbo, 186 | withProgress: charLimbo.drawingProgress 187 | ) 188 | charImage.draw(in: rect) 189 | 190 | return true 191 | } 192 | 193 | return false 194 | } 195 | 196 | skipFramesClosures["Burn\(LTMorphingPhases.skipFrames)"] = { 197 | return 1 198 | } 199 | } 200 | 201 | } 202 | -------------------------------------------------------------------------------- /LTMorphingLabel/LTMorphingLabel+Evaporate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LTMorphingLabel+Evaporate.swift 3 | // https://github.com/lexrus/LTMorphingLabel 4 | // 5 | // The MIT License (MIT) 6 | // Copyright (c) 2017 Lex Tang, http://lexrus.com 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a 9 | // copy of this software and associated documentation files 10 | // (the “Software”), to deal in the Software without restriction, 11 | // including without limitation the rights to use, copy, modify, merge, 12 | // publish, distribute, sublicense, and/or sell copies of the Software, 13 | // and to permit persons to whom the Software is furnished to do so, 14 | // subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included 17 | // in all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS 20 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 22 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 23 | // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 24 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 25 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | // 27 | 28 | import UIKit 29 | 30 | extension LTMorphingLabel { 31 | 32 | @objc 33 | func EvaporateLoad() { 34 | 35 | progressClosures["Evaporate\(LTMorphingPhases.progress)"] = { 36 | (index: Int, _, isNewChar: Bool) in 37 | let j: Int = Int(round(cos(Double(index)) * 1.2)) 38 | let delay = isNewChar ? self.morphingCharacterDelay * -1.0 : self.morphingCharacterDelay 39 | return min(1.0, max(0.0, self.morphingProgress + delay * Float(j))) 40 | } 41 | 42 | effectClosures["Evaporate\(LTMorphingPhases.disappear)"] = { 43 | char, index, progress in 44 | 45 | let newProgress = LTEasing.easeOutQuint(progress, 0.0, 1.0, 1.0) 46 | let yOffset: CGFloat = -0.8 * CGFloat(self.font.pointSize) * CGFloat(newProgress) 47 | let currentRect = self.previousRects[index].offsetBy(dx: 0, dy: yOffset) 48 | let currentAlpha = CGFloat(1.0 - newProgress) 49 | 50 | return LTCharacterLimbo( 51 | char: char, 52 | rect: currentRect, 53 | alpha: currentAlpha, 54 | size: self.font.pointSize, 55 | drawingProgress: 0.0) 56 | } 57 | 58 | effectClosures["Evaporate\(LTMorphingPhases.appear)"] = { 59 | char, index, progress in 60 | 61 | let newProgress = 1.0 - LTEasing.easeOutQuint(progress, 0.0, 1.0) 62 | let yOffset = CGFloat(self.font.pointSize) * CGFloat(newProgress) * 1.2 63 | 64 | return LTCharacterLimbo( 65 | char: char, 66 | rect: self.newRects[index].offsetBy(dx: 0, dy: yOffset), 67 | alpha: CGFloat(self.morphingProgress), 68 | size: self.font.pointSize, 69 | drawingProgress: 0.0 70 | ) 71 | } 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /LTMorphingLabel/LTMorphingLabel+Fall.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LTMorphingLabel+Fall.swift 3 | // https://github.com/lexrus/LTMorphingLabel 4 | // 5 | // The MIT License (MIT) 6 | // Copyright (c) 2017 Lex Tang, http://lexrus.com 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a 9 | // copy of this software and associated documentation files 10 | // (the “Software”), to deal in the Software without restriction, 11 | // including without limitation the rights to use, copy, modify, merge, 12 | // publish, distribute, sublicense, and/or sell copies of the Software, 13 | // and to permit persons to whom the Software is furnished to do so, 14 | // subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included 17 | // in all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS 20 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 22 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 23 | // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 24 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 25 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | // 27 | 28 | import UIKit 29 | 30 | extension LTMorphingLabel { 31 | 32 | @objc 33 | func FallLoad() { 34 | 35 | progressClosures["Fall\(LTMorphingPhases.progress)"] = { 36 | (index: Int, progress: Float, isNewChar: Bool) in 37 | 38 | if isNewChar { 39 | return min( 40 | 1.0, 41 | max( 42 | 0.0, 43 | progress 44 | - self.morphingCharacterDelay 45 | * Float(index) 46 | / 1.7 47 | ) 48 | ) 49 | } 50 | 51 | let j: Float = Float(sin(Double(index))) * 1.7 52 | return min(1.0, max(0.0001, progress + self.morphingCharacterDelay * Float(j))) 53 | 54 | } 55 | 56 | effectClosures["Fall\(LTMorphingPhases.disappear)"] = { 57 | char, index, progress in 58 | 59 | return LTCharacterLimbo( 60 | char: char, 61 | rect: self.previousRects[index], 62 | alpha: CGFloat(1.0 - progress), 63 | size: self.font.pointSize, 64 | drawingProgress: CGFloat(progress)) 65 | } 66 | 67 | effectClosures["Fall\(LTMorphingPhases.appear)"] = { 68 | char, index, progress in 69 | 70 | let currentFontSize = CGFloat( 71 | LTEasing.easeOutQuint(progress, 0.0, Float(self.font.pointSize)) 72 | ) 73 | let yOffset = CGFloat(self.font.pointSize - currentFontSize) 74 | 75 | return LTCharacterLimbo( 76 | char: char, 77 | rect: self.newRects[index].offsetBy(dx: 0, dy: yOffset), 78 | alpha: CGFloat(self.morphingProgress), 79 | size: currentFontSize, 80 | drawingProgress: 0.0 81 | ) 82 | } 83 | 84 | drawingClosures["Fall\(LTMorphingPhases.draw)"] = { 85 | limbo in 86 | 87 | if limbo.drawingProgress > 0.0 { 88 | let context = UIGraphicsGetCurrentContext() 89 | var charRect = limbo.rect 90 | context!.saveGState() 91 | let charCenterX = charRect.origin.x + (charRect.size.width / 2.0) 92 | var charBottomY = charRect.origin.y + charRect.size.height - self.font.pointSize / 6 93 | var charColor: UIColor = self.textColor 94 | 95 | // Fall down if drawingProgress is more than 50% 96 | if limbo.drawingProgress > 0.5 { 97 | let ease = CGFloat( 98 | LTEasing.easeInQuint( 99 | Float(limbo.drawingProgress - 0.4), 100 | 0.0, 101 | 1.0, 102 | 0.5 103 | ) 104 | ) 105 | charBottomY += ease * 10.0 106 | let fadeOutAlpha = min( 107 | 1.0, 108 | max( 109 | 0.0, 110 | limbo.drawingProgress * -2.0 + 2.0 + 0.01 111 | ) 112 | ) 113 | charColor = self.textColor.withAlphaComponent(fadeOutAlpha) 114 | } 115 | 116 | charRect = CGRect( 117 | x: charRect.size.width / -2.0, 118 | y: charRect.size.height * -1.0 + self.font.pointSize / 6, 119 | width: charRect.size.width, 120 | height: charRect.size.height) 121 | context!.translateBy(x: charCenterX, y: charBottomY) 122 | 123 | let angle = Float(sin(Double(limbo.rect.origin.x)) > 0.5 ? 168 : -168) 124 | let rotation = CGFloat( 125 | LTEasing.easeOutBack( 126 | min( 127 | 1.0, 128 | Float(limbo.drawingProgress) 129 | ), 130 | 0.0, 131 | 1.0 132 | ) * angle 133 | ) 134 | context!.rotate(by: rotation * CGFloat(Double.pi) / 180.0) 135 | let s = String(limbo.char) 136 | let attributes: [NSAttributedString.Key: Any] = [ 137 | .font: self.font.withSize(limbo.size), 138 | .foregroundColor: charColor 139 | ] 140 | s.draw(in: charRect, withAttributes: attributes) 141 | context!.restoreGState() 142 | 143 | return true 144 | } 145 | 146 | return false 147 | } 148 | } 149 | 150 | } 151 | -------------------------------------------------------------------------------- /LTMorphingLabel/LTMorphingLabel+Pixelate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LTMorphingLabel+Pixelate.swift 3 | // https://github.com/lexrus/LTMorphingLabel 4 | // 5 | // The MIT License (MIT) 6 | // Copyright (c) 2017 Lex Tang, http://lexrus.com 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a 9 | // copy of this software and associated documentation files 10 | // (the “Software”), to deal in the Software without restriction, 11 | // including without limitation the rights to use, copy, modify, merge, 12 | // publish, distribute, sublicense, and/or sell copies of the Software, 13 | // and to permit persons to whom the Software is furnished to do so, 14 | // subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included 17 | // in all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS 20 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 22 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 23 | // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 24 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 25 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | // 27 | 28 | import UIKit 29 | 30 | extension LTMorphingLabel { 31 | 32 | @objc 33 | func PixelateLoad() { 34 | 35 | effectClosures["Pixelate\(LTMorphingPhases.disappear)"] = { 36 | char, index, progress in 37 | 38 | return LTCharacterLimbo( 39 | char: char, 40 | rect: self.previousRects[index], 41 | alpha: CGFloat(1.0 - progress), 42 | size: self.font.pointSize, 43 | drawingProgress: CGFloat(progress)) 44 | } 45 | 46 | effectClosures["Pixelate\(LTMorphingPhases.appear)"] = { 47 | char, index, progress in 48 | 49 | return LTCharacterLimbo( 50 | char: char, 51 | rect: self.newRects[index], 52 | alpha: CGFloat(progress), 53 | size: self.font.pointSize, 54 | drawingProgress: CGFloat(1.0 - progress) 55 | ) 56 | } 57 | 58 | drawingClosures["Pixelate\(LTMorphingPhases.draw)"] = { 59 | limbo in 60 | 61 | if limbo.drawingProgress > 0.0 { 62 | 63 | let charImage = self.pixelateImageForCharLimbo( 64 | limbo, 65 | withBlurRadius: limbo.drawingProgress * 6.0 66 | ) 67 | 68 | charImage.draw(in: limbo.rect) 69 | 70 | return true 71 | } 72 | 73 | return false 74 | } 75 | } 76 | 77 | fileprivate func pixelateImageForCharLimbo( 78 | _ charLimbo: LTCharacterLimbo, 79 | withBlurRadius blurRadius: CGFloat 80 | ) -> UIImage { 81 | let scale = min(UIScreen.main.scale, 1.0 / blurRadius) 82 | UIGraphicsBeginImageContextWithOptions(charLimbo.rect.size, false, scale) 83 | let fadeOutAlpha = min(1.0, max(0.0, charLimbo.drawingProgress * -2.0 + 2.0 + 0.01)) 84 | let rect = CGRect( 85 | x: 0, 86 | y: 0, 87 | width: charLimbo.rect.size.width, 88 | height: charLimbo.rect.size.height 89 | ) 90 | String(charLimbo.char).draw(in: rect, withAttributes: [ 91 | .font: self.font as Any, 92 | .foregroundColor: self.textColor.withAlphaComponent(fadeOutAlpha) 93 | ]) 94 | guard let newImage = UIGraphicsGetImageFromCurrentImageContext() 95 | else { 96 | return UIImage() 97 | } 98 | UIGraphicsEndImageContext() 99 | return newImage 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /LTMorphingLabel/LTMorphingLabel+Sparkle.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LTMorphingLabel+Sparkle.swift 3 | // https://github.com/lexrus/LTMorphingLabel 4 | // 5 | // The MIT License (MIT) 6 | // Copyright (c) 2017 Lex Tang, http://lexrus.com 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a 9 | // copy of this software and associated documentation files 10 | // (the “Software”), to deal in the Software without restriction, 11 | // including without limitation the rights to use, copy, modify, merge, 12 | // publish, distribute, sublicense, and/or sell copies of the Software, 13 | // and to permit persons to whom the Software is furnished to do so, 14 | // subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included 17 | // in all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS 20 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 22 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 23 | // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 24 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 25 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | // 27 | 28 | import UIKit 29 | 30 | extension LTMorphingLabel { 31 | 32 | fileprivate func maskedImageForCharLimbo( 33 | _ charLimbo: LTCharacterLimbo, 34 | withProgress progress: CGFloat 35 | ) -> (UIImage, CGRect) { 36 | let maskedHeight = charLimbo.rect.size.height * max(0.01, progress) 37 | let maskedSize = CGSize( 38 | width: charLimbo.rect.size.width, 39 | height: maskedHeight 40 | ) 41 | UIGraphicsBeginImageContextWithOptions( 42 | maskedSize, 43 | false, 44 | UIScreen.main.scale 45 | ) 46 | let rect = CGRect( 47 | x: 0, 48 | y: 0, 49 | width: charLimbo.rect.size.width, 50 | height: maskedHeight 51 | ) 52 | String(charLimbo.char).draw(in: rect, withAttributes: [ 53 | .font: self.font as Any, 54 | .foregroundColor: self.textColor as Any 55 | ]) 56 | guard let newImage = UIGraphicsGetImageFromCurrentImageContext() else { 57 | return (UIImage(), CGRect.zero) 58 | } 59 | UIGraphicsEndImageContext() 60 | let newRect = CGRect( 61 | x: charLimbo.rect.origin.x, 62 | y: charLimbo.rect.origin.y, 63 | width: charLimbo.rect.size.width, 64 | height: maskedHeight 65 | ) 66 | return (newImage, newRect) 67 | } 68 | 69 | @objc 70 | func SparkleLoad() { 71 | 72 | startClosures["Sparkle\(LTMorphingPhases.start)"] = { 73 | self.emitterView.removeAllEmitters() 74 | } 75 | 76 | progressClosures["Sparkle\(LTMorphingPhases.progress)"] = { 77 | (index: Int, progress: Float, isNewChar: Bool) in 78 | 79 | if !isNewChar { 80 | return min(1.0, max(0.0, progress)) 81 | } 82 | 83 | let j = Float(sin(Float(index))) * 1.5 84 | return min( 85 | 1.0, 86 | max( 87 | 0.0001, 88 | progress + self.morphingCharacterDelay * j 89 | ) 90 | ) 91 | 92 | } 93 | 94 | effectClosures["Sparkle\(LTMorphingPhases.disappear)"] = { 95 | char, index, progress in 96 | 97 | return LTCharacterLimbo( 98 | char: char, 99 | rect: self.previousRects[index], 100 | alpha: CGFloat(1.0 - progress), 101 | size: self.font.pointSize, 102 | drawingProgress: 0.0) 103 | } 104 | 105 | effectClosures["Sparkle\(LTMorphingPhases.appear)"] = { 106 | char, index, progress in 107 | 108 | if char != " " { 109 | let rect = self.newRects[index] 110 | let emitterPosition = CGPoint( 111 | x: rect.origin.x + rect.size.width / 2.0, 112 | y: CGFloat(progress) * rect.size.height * 0.9 + rect.origin.y 113 | ) 114 | 115 | self.emitterView.createEmitter( 116 | "c\(index)", 117 | particleName: "Sparkle", 118 | duration: self.morphingDuration 119 | ) { (layer, cell) in 120 | layer.emitterSize = CGSize( 121 | width: rect.size.width, 122 | height: 1 123 | ) 124 | layer.renderMode = .unordered 125 | cell.emissionLongitude = CGFloat(Double.pi / 2.0) 126 | cell.scale = self.font.pointSize / 300.0 127 | cell.scaleSpeed = self.font.pointSize / 300.0 * -1.5 128 | cell.color = self.textColor.cgColor 129 | cell.birthRate = 130 | Float(self.font.pointSize) 131 | * Float(arc4random_uniform(7) + 3) 132 | }.update { (layer, _) in 133 | layer.emitterPosition = emitterPosition 134 | }.play() 135 | } 136 | 137 | return LTCharacterLimbo( 138 | char: char, 139 | rect: self.newRects[index], 140 | alpha: CGFloat(self.morphingProgress), 141 | size: self.font.pointSize, 142 | drawingProgress: CGFloat(progress) 143 | ) 144 | } 145 | 146 | drawingClosures["Sparkle\(LTMorphingPhases.draw)"] = { 147 | (charLimbo: LTCharacterLimbo) in 148 | 149 | if charLimbo.drawingProgress > 0.0 { 150 | 151 | let (charImage, rect) = self.maskedImageForCharLimbo( 152 | charLimbo, 153 | withProgress: charLimbo.drawingProgress 154 | ) 155 | charImage.draw(in: rect) 156 | 157 | return true 158 | } 159 | 160 | return false 161 | } 162 | 163 | skipFramesClosures["Sparkle\(LTMorphingPhases.skipFrames)"] = { 164 | return 1 165 | } 166 | } 167 | 168 | } 169 | -------------------------------------------------------------------------------- /LTMorphingLabel/LTMorphingLabel.h: -------------------------------------------------------------------------------- 1 | // 2 | // LTMorphingLabel.h 3 | // LTMorphingLabel 4 | // 5 | // Created by Lex Tang on 1/8/15. 6 | // Copyright (c) 2015 lexrus.com. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for LTMorphingLabel. 12 | FOUNDATION_EXPORT double LTMorphingLabelVersionNumber; 13 | 14 | //! Project version string for LTMorphingLabel. 15 | FOUNDATION_EXPORT const unsigned char LTMorphingLabelVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /LTMorphingLabel/LTMorphingLabel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LTMorphingLabel.swift 3 | // https://github.com/lexrus/LTMorphingLabel 4 | // 5 | // The MIT License (MIT) 6 | // Copyright (c) 2017 Lex Tang, http://lexrus.com 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a 9 | // copy of this software and associated documentation files 10 | // (the “Software”), to deal in the Software without restriction, 11 | // including without limitation the rights to use, copy, modify, merge, 12 | // publish, distribute, sublicense, and/or sell copies of the Software, 13 | // and to permit persons to whom the Software is furnished to do so, 14 | // subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included 17 | // in all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS 20 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 22 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 23 | // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 24 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 25 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | // 27 | 28 | import Foundation 29 | import UIKit 30 | import QuartzCore 31 | 32 | private func < (lhs: T?, rhs: T?) -> Bool { 33 | switch (lhs, rhs) { 34 | case let (l?, r?): 35 | return l < r 36 | case (nil, _?): 37 | return true 38 | default: 39 | return false 40 | } 41 | } 42 | 43 | private func >= (lhs: T?, rhs: T?) -> Bool { 44 | switch (lhs, rhs) { 45 | case let (l?, r?): 46 | return l >= r 47 | default: 48 | return !(lhs < rhs) 49 | } 50 | } 51 | 52 | enum LTMorphingPhases: Int { 53 | case start, appear, disappear, draw, progress, skipFrames 54 | } 55 | 56 | typealias LTMorphingStartClosure = 57 | () -> Void 58 | 59 | typealias LTMorphingEffectClosure = 60 | (Character, _ index: Int, _ progress: Float) -> LTCharacterLimbo 61 | 62 | typealias LTMorphingDrawingClosure = 63 | (LTCharacterLimbo) -> Bool 64 | 65 | typealias LTMorphingManipulateProgressClosure = 66 | (_ index: Int, _ progress: Float, _ isNewChar: Bool) -> Float 67 | 68 | typealias LTMorphingSkipFramesClosure = 69 | () -> Int 70 | 71 | @objc public protocol LTMorphingLabelDelegate { 72 | @objc optional func morphingDidStart(_ label: LTMorphingLabel) 73 | @objc optional func morphingDidComplete(_ label: LTMorphingLabel) 74 | @objc optional func morphingOnProgress(_ label: LTMorphingLabel, progress: Float) 75 | } 76 | 77 | // MARK: - LTMorphingLabel 78 | @IBDesignable open class LTMorphingLabel: UILabel { 79 | 80 | @IBInspectable open var morphingProgress: Float = 0.0 81 | @IBInspectable open var morphingDuration: Float = 0.6 82 | @IBInspectable open var morphingCharacterDelay: Float = 0.026 83 | @IBInspectable open var morphingEnabled: Bool = true 84 | 85 | @IBOutlet open weak var delegate: LTMorphingLabelDelegate? 86 | open var morphingEffect: LTMorphingEffect = .scale 87 | 88 | var startClosures = [String: LTMorphingStartClosure]() 89 | var effectClosures = [String: LTMorphingEffectClosure]() 90 | var drawingClosures = [String: LTMorphingDrawingClosure]() 91 | var progressClosures = [String: LTMorphingManipulateProgressClosure]() 92 | var skipFramesClosures = [String: LTMorphingSkipFramesClosure]() 93 | var diffResults: LTStringDiffResult? 94 | var previousText = "" 95 | 96 | var currentFrame = 0 97 | var totalFrames = 0 98 | var totalDelayFrames = 0 99 | 100 | var totalWidth: Float = 0.0 101 | var previousRects = [CGRect]() 102 | var newRects = [CGRect]() 103 | var charHeight: CGFloat = 0.0 104 | var skipFramesCount: Int = 0 105 | 106 | fileprivate var displayLink: CADisplayLink? 107 | 108 | private var tempRenderMorphingEnabled = true 109 | 110 | #if TARGET_INTERFACE_BUILDER 111 | let presentingInIB = true 112 | #else 113 | let presentingInIB = false 114 | #endif 115 | 116 | override open var font: UIFont! { 117 | get { 118 | return super.font ?? UIFont.systemFont(ofSize: 15) 119 | } 120 | set { 121 | super.font = newValue 122 | setNeedsLayout() 123 | } 124 | } 125 | 126 | override open var text: String? { 127 | get { 128 | return super.text ?? "" 129 | } 130 | set { 131 | guard text != newValue else { return } 132 | 133 | previousText = text ?? "" 134 | diffResults = previousText.diffWith(newValue) 135 | super.text = newValue ?? "" 136 | 137 | morphingProgress = 0.0 138 | currentFrame = 0 139 | totalFrames = 0 140 | 141 | tempRenderMorphingEnabled = morphingEnabled 142 | setNeedsLayout() 143 | 144 | if !morphingEnabled { 145 | return 146 | } 147 | 148 | if presentingInIB { 149 | morphingDuration = 0.01 150 | morphingProgress = 0.5 151 | } else if previousText != text { 152 | start() 153 | let closureKey = "\(morphingEffect.description)\(LTMorphingPhases.start)" 154 | if let closure = startClosures[closureKey] { 155 | return closure() 156 | } 157 | 158 | delegate?.morphingDidStart?(self) 159 | } 160 | } 161 | } 162 | 163 | open func start() { 164 | guard displayLink == nil else { return } 165 | displayLink = CADisplayLink(target: self, selector: #selector(displayFrameTick)) 166 | displayLink?.add(to: .current, forMode: .common) 167 | } 168 | 169 | open func pause() { 170 | displayLink?.isPaused = true 171 | } 172 | 173 | open func resume() { 174 | displayLink?.isPaused = false 175 | } 176 | 177 | open func finish() { 178 | displayLink?.isPaused = false 179 | } 180 | 181 | open func stop() { 182 | displayLink?.remove(from: .current, forMode: .common) 183 | displayLink?.invalidate() 184 | displayLink = nil 185 | } 186 | 187 | open var textAttributes: [NSAttributedString.Key: Any]? { 188 | didSet { 189 | setNeedsLayout() 190 | } 191 | } 192 | 193 | open override func setNeedsLayout() { 194 | super.setNeedsLayout() 195 | previousRects = rectsOfEachCharacter(previousText, withFont: font) 196 | newRects = rectsOfEachCharacter(text ?? "", withFont: font) 197 | } 198 | 199 | override open var bounds: CGRect { 200 | get { 201 | return super.bounds 202 | } 203 | set { 204 | super.bounds = newValue 205 | setNeedsLayout() 206 | } 207 | } 208 | 209 | override open var frame: CGRect { 210 | get { 211 | return super.frame 212 | } 213 | set { 214 | super.frame = newValue 215 | setNeedsLayout() 216 | } 217 | } 218 | 219 | deinit { 220 | stop() 221 | } 222 | 223 | lazy var emitterView: LTEmitterView = { 224 | let emitterView = LTEmitterView(frame: self.bounds) 225 | self.addSubview(emitterView) 226 | return emitterView 227 | }() 228 | } 229 | 230 | // MARK: - Animation extension 231 | extension LTMorphingLabel { 232 | 233 | public func updateProgress(progress: Float) { 234 | guard let displayLink = displayLink else { return } 235 | 236 | if displayLink.duration > 0.0 && totalFrames == 0 { 237 | var frameRate = Float(0) 238 | if #available(iOS 10.0, tvOS 10.0, *) { 239 | var frameInterval = 1 240 | if displayLink.preferredFramesPerSecond == 60 { 241 | frameInterval = 1 242 | } else if displayLink.preferredFramesPerSecond == 30 { 243 | frameInterval = 2 244 | } else { 245 | frameInterval = 1 246 | } 247 | frameRate = Float(displayLink.duration) / Float(frameInterval) 248 | } else { 249 | frameRate = Float(displayLink.duration) / Float(displayLink.frameInterval) 250 | } 251 | totalFrames = Int(ceil(morphingDuration / frameRate)) 252 | 253 | let totalDelay = Float((text!).count) * morphingCharacterDelay 254 | totalDelayFrames = Int(ceil(totalDelay / frameRate)) 255 | } 256 | 257 | currentFrame = Int(ceil(progress * Float(totalFrames))) 258 | 259 | if previousText != text && currentFrame < totalFrames + totalDelayFrames + 10 { 260 | morphingProgress = progress 261 | 262 | let closureKey = "\(morphingEffect.description)\(LTMorphingPhases.skipFrames)" 263 | if let closure = skipFramesClosures[closureKey] { 264 | skipFramesCount += 1 265 | if skipFramesCount > closure() { 266 | skipFramesCount = 0 267 | setNeedsDisplay() 268 | } 269 | } else { 270 | setNeedsDisplay() 271 | } 272 | 273 | if let onProgress = delegate?.morphingOnProgress { 274 | onProgress(self, morphingProgress) 275 | } 276 | } else { 277 | stop() 278 | 279 | delegate?.morphingDidComplete?(self) 280 | } 281 | } 282 | 283 | @objc func displayFrameTick() { 284 | if totalFrames == 0 { 285 | updateProgress(progress: 0) 286 | } else { 287 | morphingProgress += 1.0 / Float(totalFrames) 288 | updateProgress(progress: morphingProgress) 289 | } 290 | } 291 | 292 | // Could be enhanced by kerning text: 293 | // http://stackoverflow.com/questions/21443625/core-text-calculate-letter-frame-in-ios 294 | func rectsOfEachCharacter(_ textToDraw: String, withFont font: UIFont) -> [CGRect] { 295 | var charRects = [CGRect]() 296 | var leftOffset: CGFloat = 0.0 297 | 298 | charHeight = "Leg".size(withAttributes: [.font: font]).height 299 | 300 | let topOffset = (bounds.size.height - charHeight) / 2.0 301 | 302 | for char in textToDraw { 303 | let charSize = String(char).size(withAttributes: [.font: font]) 304 | charRects.append( 305 | CGRect( 306 | origin: CGPoint( 307 | x: leftOffset, 308 | y: topOffset 309 | ), 310 | size: charSize 311 | ) 312 | ) 313 | leftOffset += charSize.width 314 | } 315 | 316 | totalWidth = Float(leftOffset) 317 | 318 | var stringLeftOffSet: CGFloat = 0.0 319 | 320 | switch textAlignment { 321 | case .center: 322 | stringLeftOffSet = CGFloat((Float(bounds.size.width) - totalWidth) / 2.0) 323 | case .right: 324 | stringLeftOffSet = CGFloat(Float(bounds.size.width) - totalWidth) 325 | default: 326 | () 327 | } 328 | 329 | var offsetedCharRects = [CGRect]() 330 | 331 | for r in charRects { 332 | offsetedCharRects.append(r.offsetBy(dx: stringLeftOffSet, dy: 0.0)) 333 | } 334 | 335 | return offsetedCharRects 336 | } 337 | 338 | func limboOfOriginalCharacter( 339 | _ char: Character, 340 | index: Int, 341 | progress: Float) -> LTCharacterLimbo { 342 | 343 | var currentRect = previousRects[index] 344 | let oriX = Float(currentRect.origin.x) 345 | var newX = Float(currentRect.origin.x) 346 | let diffResult = diffResults!.0[index] 347 | var currentFontSize: CGFloat = font.pointSize 348 | var currentAlpha: CGFloat = 1.0 349 | 350 | switch diffResult { 351 | // Move the character that exists in the new text to current position 352 | case .same: 353 | newX = Float(newRects[index].origin.x) 354 | currentRect.origin.x = CGFloat( 355 | LTEasing.easeOutQuint(progress, oriX, newX - oriX) 356 | ) 357 | case .move(let offset): 358 | newX = Float(newRects[index + offset].origin.x) 359 | currentRect.origin.x = CGFloat( 360 | LTEasing.easeOutQuint(progress, oriX, newX - oriX) 361 | ) 362 | case .moveAndAdd(let offset): 363 | newX = Float(newRects[index + offset].origin.x) 364 | currentRect.origin.x = CGFloat( 365 | LTEasing.easeOutQuint(progress, oriX, newX - oriX) 366 | ) 367 | default: 368 | // Otherwise, remove it 369 | 370 | // Override morphing effect with closure in extenstions 371 | if let closure = effectClosures[ 372 | "\(morphingEffect.description)\(LTMorphingPhases.disappear)" 373 | ] { 374 | return closure(char, index, progress) 375 | } else { 376 | // And scale it by default 377 | let fontEase = CGFloat( 378 | LTEasing.easeOutQuint( 379 | progress, 0, Float(font.pointSize) 380 | ) 381 | ) 382 | // For emojis 383 | currentFontSize = max(0.0001, font.pointSize - fontEase) 384 | currentAlpha = CGFloat(1.0 - progress) 385 | currentRect = previousRects[index].offsetBy( 386 | dx: 0, 387 | dy: CGFloat(font.pointSize - currentFontSize) 388 | ) 389 | } 390 | } 391 | 392 | return LTCharacterLimbo( 393 | char: char, 394 | rect: currentRect, 395 | alpha: currentAlpha, 396 | size: currentFontSize, 397 | drawingProgress: 0.0 398 | ) 399 | } 400 | 401 | func limboOfNewCharacter( 402 | _ char: Character, 403 | index: Int, 404 | progress: Float) -> LTCharacterLimbo { 405 | 406 | let currentRect = newRects[index] 407 | var currentFontSize = CGFloat( 408 | LTEasing.easeOutQuint(progress, 0, Float(font.pointSize)) 409 | ) 410 | 411 | if let closure = effectClosures[ 412 | "\(morphingEffect.description)\(LTMorphingPhases.appear)" 413 | ] { 414 | return closure(char, index, progress) 415 | } else { 416 | currentFontSize = CGFloat( 417 | LTEasing.easeOutQuint(progress, 0.0, Float(font.pointSize)) 418 | ) 419 | // For emojis 420 | currentFontSize = max(0.0001, currentFontSize) 421 | 422 | let yOffset = CGFloat(font.pointSize - currentFontSize) 423 | 424 | return LTCharacterLimbo( 425 | char: char, 426 | rect: currentRect.offsetBy(dx: 0, dy: yOffset), 427 | alpha: CGFloat(morphingProgress), 428 | size: currentFontSize, 429 | drawingProgress: 0.0 430 | ) 431 | } 432 | } 433 | 434 | func limboOfCharacters() -> [LTCharacterLimbo] { 435 | var limbo = [LTCharacterLimbo]() 436 | 437 | // Iterate original characters 438 | for (i, character) in previousText.enumerated() { 439 | var progress: Float = 0.0 440 | 441 | if let closure = progressClosures[ 442 | "\(morphingEffect.description)\(LTMorphingPhases.progress)" 443 | ] { 444 | progress = closure(i, morphingProgress, false) 445 | } else { 446 | progress = min(1.0, max(0.0, morphingProgress + morphingCharacterDelay * Float(i))) 447 | } 448 | 449 | let limboOfCharacter = limboOfOriginalCharacter(character, index: i, progress: progress) 450 | limbo.append(limboOfCharacter) 451 | } 452 | 453 | // Add new characters 454 | for (i, character) in (text!).enumerated() { 455 | if i >= diffResults?.0.count { 456 | break 457 | } 458 | 459 | var progress: Float = 0.0 460 | 461 | if let closure = progressClosures[ 462 | "\(morphingEffect.description)\(LTMorphingPhases.progress)" 463 | ] { 464 | progress = closure(i, morphingProgress, true) 465 | } else { 466 | progress = min(1.0, max(0.0, morphingProgress - morphingCharacterDelay * Float(i))) 467 | } 468 | 469 | // Don't draw character that already exists 470 | if diffResults?.skipDrawingResults[i] == true { 471 | continue 472 | } 473 | 474 | if let diffResult = diffResults?.0[i] { 475 | switch diffResult { 476 | case .moveAndAdd, .replace, .add, .delete: 477 | let limboOfCharacter = limboOfNewCharacter( 478 | character, 479 | index: i, 480 | progress: progress 481 | ) 482 | limbo.append(limboOfCharacter) 483 | default: 484 | () 485 | } 486 | } 487 | } 488 | 489 | return limbo 490 | } 491 | 492 | } 493 | 494 | // MARK: - Drawing extension 495 | extension LTMorphingLabel { 496 | 497 | override open func didMoveToSuperview() { 498 | guard nil != superview else { 499 | stop() 500 | return 501 | } 502 | 503 | if let s = text { 504 | text = s 505 | } 506 | 507 | // Load all morphing effects 508 | for effectName: String in LTMorphingEffect.allValues { 509 | let effectFunc = Selector("\(effectName)Load") 510 | if responds(to: effectFunc) { 511 | perform(effectFunc) 512 | } 513 | } 514 | } 515 | 516 | override open func drawText(in rect: CGRect) { 517 | if !tempRenderMorphingEnabled || limboOfCharacters().count == 0 { 518 | super.drawText(in: rect) 519 | return 520 | } 521 | 522 | for charLimbo in limboOfCharacters() { 523 | let charRect = charLimbo.rect 524 | 525 | let willAvoidDefaultDrawing: Bool = { 526 | if let closure = drawingClosures[ 527 | "\(morphingEffect.description)\(LTMorphingPhases.draw)" 528 | ] { 529 | return closure($0) 530 | } 531 | return false 532 | }(charLimbo) 533 | 534 | if !willAvoidDefaultDrawing { 535 | var attrs: [NSAttributedString.Key: Any] = [ 536 | .foregroundColor: textColor.withAlphaComponent(charLimbo.alpha) 537 | ] 538 | 539 | attrs[.font] = UIFont(descriptor: font.fontDescriptor, size: charLimbo.size) 540 | 541 | for (key, value) in textAttributes ?? [:] { 542 | attrs[key] = value 543 | } 544 | 545 | let s = String(charLimbo.char) 546 | s.draw(in: charRect, withAttributes: attrs) 547 | } 548 | } 549 | } 550 | 551 | } 552 | -------------------------------------------------------------------------------- /LTMorphingLabel/LTStringDiffResult.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LTStringDiffResult.swift 3 | // https://github.com/lexrus/LTMorphingLabel 4 | // 5 | // The MIT License (MIT) 6 | // Copyright (c) 2017 Lex Tang, http://lexrus.com 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a 9 | // copy of this software and associated documentation files 10 | // (the “Software”), to deal in the Software without restriction, 11 | // including without limitation the rights to use, copy, modify, merge, 12 | // publish, distribute, sublicense, and/or sell copies of the Software, 13 | // and to permit persons to whom the Software is furnished to do so, 14 | // subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included 17 | // in all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS 20 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 22 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 23 | // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 24 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 25 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | // 27 | 28 | import Foundation 29 | 30 | public typealias LTStringDiffResult = ([LTCharacterDiffResult], skipDrawingResults: [Bool]) 31 | 32 | public extension String { 33 | 34 | func diffWith(_ anotherString: String?) -> LTStringDiffResult { 35 | 36 | guard let anotherString = anotherString else { 37 | let diffResults: [LTCharacterDiffResult] = 38 | Array(repeating: .delete, count: self.count) 39 | let skipDrawingResults: [Bool] = Array(repeating: false, count: self.count) 40 | return (diffResults, skipDrawingResults) 41 | } 42 | 43 | let newChars = anotherString.enumerated() 44 | let lhsLength = self.count 45 | let rhsLength = anotherString.count 46 | var skipIndexes = [Int]() 47 | let leftChars = Array(self) 48 | 49 | let maxLength = max(lhsLength, rhsLength) 50 | var diffResults: [LTCharacterDiffResult] = Array(repeating: .add, count: maxLength) 51 | var skipDrawingResults: [Bool] = Array(repeating: false, count: maxLength) 52 | 53 | for i in 0.. lhsLength - 1 { 56 | continue 57 | } 58 | 59 | let leftChar = leftChars[i] 60 | 61 | // Search left character in the new string 62 | var foundCharacterInRhs = false 63 | for (j, newChar) in newChars { 64 | if skipIndexes.contains(j) || leftChar != newChar { 65 | continue 66 | } 67 | 68 | skipIndexes.append(j) 69 | foundCharacterInRhs = true 70 | if i == j { 71 | // Character not changed 72 | diffResults[i] = .same 73 | } else { 74 | // foundCharacterInRhs and move 75 | let offset = j - i 76 | 77 | if i <= rhsLength - 1 { 78 | // Move to a new index and add a new character to new original place 79 | diffResults[i] = .moveAndAdd(offset: offset) 80 | } else { 81 | diffResults[i] = .move(offset: offset) 82 | } 83 | 84 | skipDrawingResults[j] = true 85 | } 86 | break 87 | } 88 | 89 | if !foundCharacterInRhs { 90 | if i < rhsLength - 1 { 91 | diffResults[i] = .replace 92 | } else { 93 | diffResults[i] = .delete 94 | } 95 | } 96 | } 97 | 98 | return (diffResults, skipDrawingResults) 99 | 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /LTMorphingLabel/Particles/Fire.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lexrus/LTMorphingLabel/4b80cf8b8ce04f15f8fa0581fd87420e09d4408f/LTMorphingLabel/Particles/Fire.png -------------------------------------------------------------------------------- /LTMorphingLabel/Particles/Fragment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lexrus/LTMorphingLabel/4b80cf8b8ce04f15f8fa0581fd87420e09d4408f/LTMorphingLabel/Particles/Fragment.png -------------------------------------------------------------------------------- /LTMorphingLabel/Particles/Smoke.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lexrus/LTMorphingLabel/4b80cf8b8ce04f15f8fa0581fd87420e09d4408f/LTMorphingLabel/Particles/Smoke.png -------------------------------------------------------------------------------- /LTMorphingLabel/Particles/Sparkle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lexrus/LTMorphingLabel/4b80cf8b8ce04f15f8fa0581fd87420e09d4408f/LTMorphingLabel/Particles/Sparkle.png -------------------------------------------------------------------------------- /LTMorphingLabel/SwiftUI/MorphingText.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MorphingText.swift 3 | // LTMorphingLabel 4 | // 5 | // Created by Lex on 2020/5/10. 6 | // Copyright © 2020 lexrus.com. All rights reserved. 7 | // 8 | 9 | #if canImport(SwiftUI) 10 | import SwiftUI 11 | #endif 12 | 13 | @available(iOS 13.0.0, *) 14 | public struct MorphingText: UIViewRepresentable { 15 | public typealias UIViewType = LTMorphingLabel 16 | 17 | var text: String 18 | var morphingEffect: LTMorphingEffect 19 | var moprhingEnabled: Bool = true 20 | var font: UIFont 21 | var textColor: UIColor 22 | var textAlignment: NSTextAlignment 23 | 24 | public init(_ text: String, 25 | effect: LTMorphingEffect = .scale, 26 | font: UIFont = .systemFont(ofSize: 16), 27 | textColor: UIColor = .black, 28 | textAlignment: NSTextAlignment = .center 29 | ) { 30 | self.text = text 31 | self.morphingEffect = effect 32 | self.font = font 33 | self.textColor = textColor 34 | self.textAlignment = textAlignment 35 | } 36 | 37 | public func makeUIView(context: Context) -> UIViewType { 38 | let label = LTMorphingLabel(frame: CGRect(origin: .zero, size: CGSize(width: 200, height: 50))) 39 | label.textAlignment = .center 40 | label.textColor = UIColor.white 41 | return label 42 | } 43 | 44 | public func updateUIView(_ uiView: UIViewType, context: Context) { 45 | uiView.text = text 46 | uiView.font = font 47 | uiView.morphingEnabled = moprhingEnabled 48 | uiView.morphingEffect = morphingEffect 49 | uiView.textColor = textColor 50 | uiView.textAlignment = textAlignment 51 | } 52 | } 53 | 54 | @available(iOS 13.0.0, *) 55 | struct MorphingText_Previews: PreviewProvider { 56 | 57 | static var previews: some View { 58 | PreviewWrapper() 59 | .previewDevice("iPhone SE") 60 | .environment(\.colorScheme, .dark) 61 | } 62 | 63 | struct PreviewWrapper: View { 64 | @State var morphingEnabled = true 65 | @State var textIndex: Double = 0 66 | @State var effectIndex: Double = 0 67 | 68 | private var textArray = [ 69 | "Supercalifragilisticexpialidocious", 70 | "Stay hungry, stay foolish.", 71 | "Love what you do and do what you love.", 72 | "Apple doesn't do hobbies as a general rule.", 73 | "iOS", 74 | "iPhone", 75 | "iPhone 11", 76 | "iPhone 11 Pro", 77 | "iPhone 11 Pro Max", 78 | "$9995.00", 79 | "$9996.00", 80 | "$9997.00", 81 | "$9998.00", 82 | "$9999.00", 83 | "$10000.00" 84 | ] 85 | 86 | public var body: some View { 87 | VStack { 88 | Spacer() 89 | Text("LTMorphingLabel").font(.largeTitle) 90 | Text("for SwiftUI").font(.title) 91 | 92 | MorphingText( 93 | textArray[Int(round(textIndex))], 94 | effect: LTMorphingEffect.allCases[Int(round(effectIndex))], 95 | font: UIFont.systemFont(ofSize: 20), 96 | textColor: .white 97 | ) 98 | .frame(maxWidth: 200, maxHeight: 100) 99 | 100 | Text("Effect: \(LTMorphingEffect.allCases[Int(round(effectIndex))].description)").font(.title) 101 | Slider(value: $effectIndex, in: 0...Double(LTMorphingEffect.allCases.count - 1)) 102 | 103 | Slider(value: $textIndex, in: 0...Double(textArray.count - 1)) 104 | 105 | Toggle("MorphingEnabled", isOn: $morphingEnabled) 106 | Spacer() 107 | } 108 | .padding(20) 109 | .foregroundColor(Color.white) 110 | .background(Color.black) 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /LTMorphingLabel/tvOS-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /LTMorphingLabelDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 52; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | D701040D2516F5E500DB98F7 /* MorphingText.swift in Sources */ = {isa = PBXBuildFile; fileRef = D764373D2467A01A0037C36B /* MorphingText.swift */; platformFilter = maccatalyst; }; 11 | D720D06B1A5E91CB00747465 /* LTEasing.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7AFEC911962511C0006FA88 /* LTEasing.swift */; }; 12 | D720D06C1A5E91CB00747465 /* LTStringDiffResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7633A2C195965A00009D084 /* LTStringDiffResult.swift */; }; 13 | D720D06D1A5E91CB00747465 /* LTMorphingLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D74AD98E19517430009A2ACD /* LTMorphingLabel.swift */; }; 14 | D720D06E1A5E91CB00747465 /* LTMorphingLabel+Evaporate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7AFEC93196256720006FA88 /* LTMorphingLabel+Evaporate.swift */; }; 15 | D720D06F1A5E91CB00747465 /* LTMorphingLabel+Fall.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7AFEC97196280860006FA88 /* LTMorphingLabel+Fall.swift */; }; 16 | D720D0701A5E91CB00747465 /* LTMorphingLabel+Pixelate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7558D391962AB3000DCF592 /* LTMorphingLabel+Pixelate.swift */; }; 17 | D720D0711A5E91CB00747465 /* LTMorphingLabel+Sparkle.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7EC5FD5196692E8004E7076 /* LTMorphingLabel+Sparkle.swift */; }; 18 | D720D0721A5E91CB00747465 /* LTMorphingLabel+Burn.swift in Sources */ = {isa = PBXBuildFile; fileRef = D757C30C1974243400A34565 /* LTMorphingLabel+Burn.swift */; }; 19 | D720D0731A5E91CB00747465 /* LTMorphingLabel+Anvil.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7C98F381975265C00902C5C /* LTMorphingLabel+Anvil.swift */; }; 20 | D720D0741A5E923100747465 /* LTEasing.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7AFEC911962511C0006FA88 /* LTEasing.swift */; }; 21 | D720D0751A5E923100747465 /* LTStringDiffResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7633A2C195965A00009D084 /* LTStringDiffResult.swift */; }; 22 | D720D0761A5E923100747465 /* LTMorphingLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D74AD98E19517430009A2ACD /* LTMorphingLabel.swift */; }; 23 | D720D0771A5E923100747465 /* LTMorphingLabel+Evaporate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7AFEC93196256720006FA88 /* LTMorphingLabel+Evaporate.swift */; }; 24 | D720D0781A5E923100747465 /* LTMorphingLabel+Fall.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7AFEC97196280860006FA88 /* LTMorphingLabel+Fall.swift */; }; 25 | D720D0791A5E923100747465 /* LTMorphingLabel+Pixelate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7558D391962AB3000DCF592 /* LTMorphingLabel+Pixelate.swift */; }; 26 | D720D07A1A5E923100747465 /* LTMorphingLabel+Sparkle.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7EC5FD5196692E8004E7076 /* LTMorphingLabel+Sparkle.swift */; }; 27 | D720D07B1A5E923100747465 /* LTMorphingLabel+Burn.swift in Sources */ = {isa = PBXBuildFile; fileRef = D757C30C1974243400A34565 /* LTMorphingLabel+Burn.swift */; }; 28 | D720D07C1A5E923100747465 /* LTMorphingLabel+Anvil.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7C98F381975265C00902C5C /* LTMorphingLabel+Anvil.swift */; }; 29 | D720D07D1A5E923800747465 /* Fire.png in Resources */ = {isa = PBXBuildFile; fileRef = D757C31619742B1D00A34565 /* Fire.png */; }; 30 | D720D07E1A5E923800747465 /* Sparkle.png in Resources */ = {isa = PBXBuildFile; fileRef = D723B2CB196A3FD100B1BBE8 /* Sparkle.png */; }; 31 | D720D07F1A5E923800747465 /* Smoke.png in Resources */ = {isa = PBXBuildFile; fileRef = D7C98F3619750F6900902C5C /* Smoke.png */; }; 32 | D720D0801A5E923800747465 /* Fragment.png in Resources */ = {isa = PBXBuildFile; fileRef = D7C98F3A1975298000902C5C /* Fragment.png */; }; 33 | D72A94E01F7CAF8B007E5157 /* MorphingLabel.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D7D4B01F1A5E87D50087F83D /* MorphingLabel.framework */; }; 34 | D72A94E11F7CAFA5007E5157 /* MorphingLabel.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D7D4B01F1A5E87D50087F83D /* MorphingLabel.framework */; }; 35 | D73C19FA1AC2824100C1A824 /* MorphingLabel.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D7D4B01F1A5E87D50087F83D /* MorphingLabel.framework */; }; 36 | D73DFD021F4DB5CC00F84122 /* LTCharacterLimbo.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7EF4F061AB5612C00D46A0E /* LTCharacterLimbo.swift */; }; 37 | D73DFD031F4DB5CC00F84122 /* LTEasing.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7AFEC911962511C0006FA88 /* LTEasing.swift */; }; 38 | D73DFD041F4DB5CC00F84122 /* LTStringDiffResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7633A2C195965A00009D084 /* LTStringDiffResult.swift */; }; 39 | D73DFD051F4DB5CC00F84122 /* LTMorphingEffect.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7EF4F031AB5608000D46A0E /* LTMorphingEffect.swift */; }; 40 | D73DFD061F4DB5CC00F84122 /* LTMorphingLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D74AD98E19517430009A2ACD /* LTMorphingLabel.swift */; }; 41 | D73DFD071F4DB5CC00F84122 /* LTCharacterDiffResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = D797F6F81D2E992200531960 /* LTCharacterDiffResult.swift */; }; 42 | D73DFD081F4DB5CC00F84122 /* LTMorphingLabel+Evaporate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7AFEC93196256720006FA88 /* LTMorphingLabel+Evaporate.swift */; }; 43 | D73DFD091F4DB5CC00F84122 /* LTMorphingLabel+Fall.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7AFEC97196280860006FA88 /* LTMorphingLabel+Fall.swift */; }; 44 | D73DFD0A1F4DB5CC00F84122 /* LTMorphingLabel+Pixelate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7558D391962AB3000DCF592 /* LTMorphingLabel+Pixelate.swift */; }; 45 | D73DFD0B1F4DB5CC00F84122 /* LTMorphingLabel+Sparkle.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7EC5FD5196692E8004E7076 /* LTMorphingLabel+Sparkle.swift */; }; 46 | D73DFD0C1F4DB5CC00F84122 /* LTMorphingLabel+Burn.swift in Sources */ = {isa = PBXBuildFile; fileRef = D757C30C1974243400A34565 /* LTMorphingLabel+Burn.swift */; }; 47 | D73DFD0D1F4DB5CC00F84122 /* LTMorphingLabel+Anvil.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7C98F381975265C00902C5C /* LTMorphingLabel+Anvil.swift */; }; 48 | D73DFD0E1F4DB5CC00F84122 /* LTEmitterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7EF4F091AB561A400D46A0E /* LTEmitterView.swift */; }; 49 | D73DFD111F4DB5CC00F84122 /* LTMorphingLabel.h in Headers */ = {isa = PBXBuildFile; fileRef = D7D4B0231A5E87D50087F83D /* LTMorphingLabel.h */; settings = {ATTRIBUTES = (Public, ); }; }; 50 | D73DFD131F4DB5CC00F84122 /* Fire.png in Resources */ = {isa = PBXBuildFile; fileRef = D757C31619742B1D00A34565 /* Fire.png */; }; 51 | D73DFD141F4DB5CC00F84122 /* Sparkle.png in Resources */ = {isa = PBXBuildFile; fileRef = D723B2CB196A3FD100B1BBE8 /* Sparkle.png */; }; 52 | D73DFD151F4DB5CC00F84122 /* Smoke.png in Resources */ = {isa = PBXBuildFile; fileRef = D7C98F3619750F6900902C5C /* Smoke.png */; }; 53 | D73DFD161F4DB5CC00F84122 /* Fragment.png in Resources */ = {isa = PBXBuildFile; fileRef = D7C98F3A1975298000902C5C /* Fragment.png */; }; 54 | D73DFD231F5056A700F84122 /* LTMorphingLabelUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73DFD221F5056A700F84122 /* LTMorphingLabelUITests.swift */; }; 55 | D74AD974195171E7009A2ACD /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D74AD973195171E7009A2ACD /* AppDelegate.swift */; }; 56 | D74AD976195171E7009A2ACD /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D74AD975195171E7009A2ACD /* Images.xcassets */; }; 57 | D74AD98D195173B5009A2ACD /* LTMorphingLabelDemo.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D74AD98C195173B5009A2ACD /* LTMorphingLabelDemo.storyboard */; }; 58 | D7633A2B195812020009D084 /* LTDemoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7633A2A195812020009D084 /* LTDemoViewController.swift */; }; 59 | D7934DCB1A63AF1100AE04CE /* LTMorphingLabelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7934DCA1A63AF1100AE04CE /* LTMorphingLabelTests.swift */; }; 60 | D797F6F91D2E992200531960 /* LTCharacterDiffResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = D797F6F81D2E992200531960 /* LTCharacterDiffResult.swift */; }; 61 | D797F6FA1D2EA47A00531960 /* LTCharacterDiffResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = D797F6F81D2E992200531960 /* LTCharacterDiffResult.swift */; }; 62 | D7D4B0241A5E87D50087F83D /* LTMorphingLabel.h in Headers */ = {isa = PBXBuildFile; fileRef = D7D4B0231A5E87D50087F83D /* LTMorphingLabel.h */; settings = {ATTRIBUTES = (Public, ); }; }; 63 | D7D4B0381A5E87D50087F83D /* MorphingLabel.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D7D4B01F1A5E87D50087F83D /* MorphingLabel.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 64 | D7D4B03F1A5E8F6E0087F83D /* Fire.png in Resources */ = {isa = PBXBuildFile; fileRef = D757C31619742B1D00A34565 /* Fire.png */; }; 65 | D7D4B0401A5E8F6E0087F83D /* Sparkle.png in Resources */ = {isa = PBXBuildFile; fileRef = D723B2CB196A3FD100B1BBE8 /* Sparkle.png */; }; 66 | D7D4B0411A5E8F6E0087F83D /* Smoke.png in Resources */ = {isa = PBXBuildFile; fileRef = D7C98F3619750F6900902C5C /* Smoke.png */; }; 67 | D7D4B0421A5E8F6E0087F83D /* Fragment.png in Resources */ = {isa = PBXBuildFile; fileRef = D7C98F3A1975298000902C5C /* Fragment.png */; }; 68 | D7E537321B8CADF6008EF6D1 /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D7E537311B8CADF6008EF6D1 /* Launch Screen.storyboard */; }; 69 | D7EF4F041AB5608000D46A0E /* LTMorphingEffect.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7EF4F031AB5608000D46A0E /* LTMorphingEffect.swift */; }; 70 | D7EF4F051AB5610200D46A0E /* LTMorphingEffect.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7EF4F031AB5608000D46A0E /* LTMorphingEffect.swift */; }; 71 | D7EF4F071AB5612C00D46A0E /* LTCharacterLimbo.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7EF4F061AB5612C00D46A0E /* LTCharacterLimbo.swift */; }; 72 | D7EF4F081AB5612C00D46A0E /* LTCharacterLimbo.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7EF4F061AB5612C00D46A0E /* LTCharacterLimbo.swift */; }; 73 | D7EF4F0A1AB561A400D46A0E /* LTEmitterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7EF4F091AB561A400D46A0E /* LTEmitterView.swift */; }; 74 | D7EF4F0B1AB561A400D46A0E /* LTEmitterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7EF4F091AB561A400D46A0E /* LTEmitterView.swift */; }; 75 | /* End PBXBuildFile section */ 76 | 77 | /* Begin PBXContainerItemProxy section */ 78 | D72A94DD1F7CAF6D007E5157 /* PBXContainerItemProxy */ = { 79 | isa = PBXContainerItemProxy; 80 | containerPortal = D74AD966195171E7009A2ACD /* Project object */; 81 | proxyType = 1; 82 | remoteGlobalIDString = D7D4B01E1A5E87D50087F83D; 83 | remoteInfo = LTMorphingLabel; 84 | }; 85 | D72A94E21F7CAFAA007E5157 /* PBXContainerItemProxy */ = { 86 | isa = PBXContainerItemProxy; 87 | containerPortal = D74AD966195171E7009A2ACD /* Project object */; 88 | proxyType = 1; 89 | remoteGlobalIDString = D7D4B01E1A5E87D50087F83D; 90 | remoteInfo = LTMorphingLabel; 91 | }; 92 | D73C19F81AC2823900C1A824 /* PBXContainerItemProxy */ = { 93 | isa = PBXContainerItemProxy; 94 | containerPortal = D74AD966195171E7009A2ACD /* Project object */; 95 | proxyType = 1; 96 | remoteGlobalIDString = D7D4B01E1A5E87D50087F83D; 97 | remoteInfo = LTMorphingLabel; 98 | }; 99 | D73C19FB1AC28DAA00C1A824 /* PBXContainerItemProxy */ = { 100 | isa = PBXContainerItemProxy; 101 | containerPortal = D74AD966195171E7009A2ACD /* Project object */; 102 | proxyType = 1; 103 | remoteGlobalIDString = D74AD96D195171E7009A2ACD; 104 | remoteInfo = LTMorphingLabelDemo; 105 | }; 106 | D73DFD251F5056A700F84122 /* PBXContainerItemProxy */ = { 107 | isa = PBXContainerItemProxy; 108 | containerPortal = D74AD966195171E7009A2ACD /* Project object */; 109 | proxyType = 1; 110 | remoteGlobalIDString = D74AD96D195171E7009A2ACD; 111 | remoteInfo = LTMorphingLabelDemo; 112 | }; 113 | /* End PBXContainerItemProxy section */ 114 | 115 | /* Begin PBXCopyFilesBuildPhase section */ 116 | D7D4B0371A5E87D50087F83D /* Embed Frameworks */ = { 117 | isa = PBXCopyFilesBuildPhase; 118 | buildActionMask = 2147483647; 119 | dstPath = ""; 120 | dstSubfolderSpec = 10; 121 | files = ( 122 | D7D4B0381A5E87D50087F83D /* MorphingLabel.framework in Embed Frameworks */, 123 | ); 124 | name = "Embed Frameworks"; 125 | runOnlyForDeploymentPostprocessing = 0; 126 | }; 127 | /* End PBXCopyFilesBuildPhase section */ 128 | 129 | /* Begin PBXFileReference section */ 130 | D723B2CB196A3FD100B1BBE8 /* Sparkle.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Sparkle.png; sourceTree = ""; }; 131 | D72CE5F420863C9D005BBDEF /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 132 | D73DFD1A1F4DB5CC00F84122 /* MorphingLabel.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MorphingLabel.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 133 | D73DFD1B1F4DB5CC00F84122 /* tvOS-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "tvOS-Info.plist"; path = "/Users/lex/git/LTMorphingLabel/LTMorphingLabel/tvOS-Info.plist"; sourceTree = ""; }; 134 | D73DFD201F5056A700F84122 /* LTMorphingLabelUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = LTMorphingLabelUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 135 | D73DFD221F5056A700F84122 /* LTMorphingLabelUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LTMorphingLabelUITests.swift; sourceTree = ""; }; 136 | D73DFD241F5056A700F84122 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 137 | D74AD96E195171E7009A2ACD /* Morphing.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Morphing.app; sourceTree = BUILT_PRODUCTS_DIR; }; 138 | D74AD972195171E7009A2ACD /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 139 | D74AD973195171E7009A2ACD /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 140 | D74AD975195171E7009A2ACD /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 141 | D74AD98C195173B5009A2ACD /* LTMorphingLabelDemo.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LTMorphingLabelDemo.storyboard; sourceTree = ""; }; 142 | D74AD98E19517430009A2ACD /* LTMorphingLabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LTMorphingLabel.swift; sourceTree = ""; }; 143 | D7558D391962AB3000DCF592 /* LTMorphingLabel+Pixelate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "LTMorphingLabel+Pixelate.swift"; sourceTree = ""; }; 144 | D757C30C1974243400A34565 /* LTMorphingLabel+Burn.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "LTMorphingLabel+Burn.swift"; sourceTree = ""; }; 145 | D757C31619742B1D00A34565 /* Fire.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Fire.png; sourceTree = ""; }; 146 | D7633A2A195812020009D084 /* LTDemoViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LTDemoViewController.swift; sourceTree = ""; }; 147 | D7633A2C195965A00009D084 /* LTStringDiffResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LTStringDiffResult.swift; sourceTree = ""; }; 148 | D764373D2467A01A0037C36B /* MorphingText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MorphingText.swift; sourceTree = ""; }; 149 | D7934DC61A63AF1100AE04CE /* LTMorphingLabelTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = LTMorphingLabelTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 150 | D7934DC91A63AF1100AE04CE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 151 | D7934DCA1A63AF1100AE04CE /* LTMorphingLabelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LTMorphingLabelTests.swift; sourceTree = ""; }; 152 | D797F6F81D2E992200531960 /* LTCharacterDiffResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LTCharacterDiffResult.swift; sourceTree = ""; }; 153 | D7AA544125170E1100299291 /* Package.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = ""; }; 154 | D7AFEC911962511C0006FA88 /* LTEasing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LTEasing.swift; sourceTree = ""; }; 155 | D7AFEC93196256720006FA88 /* LTMorphingLabel+Evaporate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "LTMorphingLabel+Evaporate.swift"; sourceTree = ""; }; 156 | D7AFEC97196280860006FA88 /* LTMorphingLabel+Fall.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "LTMorphingLabel+Fall.swift"; sourceTree = ""; }; 157 | D7C98F3619750F6900902C5C /* Smoke.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Smoke.png; sourceTree = ""; }; 158 | D7C98F381975265C00902C5C /* LTMorphingLabel+Anvil.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "LTMorphingLabel+Anvil.swift"; sourceTree = ""; }; 159 | D7C98F3A1975298000902C5C /* Fragment.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Fragment.png; sourceTree = ""; }; 160 | D7D4B01F1A5E87D50087F83D /* MorphingLabel.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MorphingLabel.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 161 | D7D4B0221A5E87D50087F83D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 162 | D7D4B0231A5E87D50087F83D /* LTMorphingLabel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LTMorphingLabel.h; sourceTree = ""; }; 163 | D7E537311B8CADF6008EF6D1 /* Launch Screen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = "Launch Screen.storyboard"; sourceTree = ""; }; 164 | D7EC5FD5196692E8004E7076 /* LTMorphingLabel+Sparkle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "LTMorphingLabel+Sparkle.swift"; sourceTree = ""; }; 165 | D7EF4F031AB5608000D46A0E /* LTMorphingEffect.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LTMorphingEffect.swift; sourceTree = ""; }; 166 | D7EF4F061AB5612C00D46A0E /* LTCharacterLimbo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LTCharacterLimbo.swift; sourceTree = ""; }; 167 | D7EF4F091AB561A400D46A0E /* LTEmitterView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LTEmitterView.swift; sourceTree = ""; }; 168 | /* End PBXFileReference section */ 169 | 170 | /* Begin PBXFrameworksBuildPhase section */ 171 | D73DFD0F1F4DB5CC00F84122 /* Frameworks */ = { 172 | isa = PBXFrameworksBuildPhase; 173 | buildActionMask = 2147483647; 174 | files = ( 175 | ); 176 | runOnlyForDeploymentPostprocessing = 0; 177 | }; 178 | D73DFD1D1F5056A700F84122 /* Frameworks */ = { 179 | isa = PBXFrameworksBuildPhase; 180 | buildActionMask = 2147483647; 181 | files = ( 182 | D72A94E11F7CAFA5007E5157 /* MorphingLabel.framework in Frameworks */, 183 | ); 184 | runOnlyForDeploymentPostprocessing = 0; 185 | }; 186 | D74AD96B195171E7009A2ACD /* Frameworks */ = { 187 | isa = PBXFrameworksBuildPhase; 188 | buildActionMask = 2147483647; 189 | files = ( 190 | D72A94E01F7CAF8B007E5157 /* MorphingLabel.framework in Frameworks */, 191 | ); 192 | runOnlyForDeploymentPostprocessing = 0; 193 | }; 194 | D7934DC31A63AF1100AE04CE /* Frameworks */ = { 195 | isa = PBXFrameworksBuildPhase; 196 | buildActionMask = 2147483647; 197 | files = ( 198 | D73C19FA1AC2824100C1A824 /* MorphingLabel.framework in Frameworks */, 199 | ); 200 | runOnlyForDeploymentPostprocessing = 0; 201 | }; 202 | D7D4B01B1A5E87D50087F83D /* Frameworks */ = { 203 | isa = PBXFrameworksBuildPhase; 204 | buildActionMask = 2147483647; 205 | files = ( 206 | ); 207 | runOnlyForDeploymentPostprocessing = 0; 208 | }; 209 | /* End PBXFrameworksBuildPhase section */ 210 | 211 | /* Begin PBXGroup section */ 212 | D70FB1FB19685C3300EF9B84 /* Particles */ = { 213 | isa = PBXGroup; 214 | children = ( 215 | D757C31619742B1D00A34565 /* Fire.png */, 216 | D723B2CB196A3FD100B1BBE8 /* Sparkle.png */, 217 | D7C98F3619750F6900902C5C /* Smoke.png */, 218 | D7C98F3A1975298000902C5C /* Fragment.png */, 219 | ); 220 | path = Particles; 221 | sourceTree = ""; 222 | }; 223 | D72A94DF1F7CAF8B007E5157 /* Frameworks */ = { 224 | isa = PBXGroup; 225 | children = ( 226 | ); 227 | name = Frameworks; 228 | sourceTree = ""; 229 | }; 230 | D73DFD211F5056A700F84122 /* LTMorphingLabelUITests */ = { 231 | isa = PBXGroup; 232 | children = ( 233 | D73DFD221F5056A700F84122 /* LTMorphingLabelUITests.swift */, 234 | D73DFD241F5056A700F84122 /* Info.plist */, 235 | ); 236 | path = LTMorphingLabelUITests; 237 | sourceTree = ""; 238 | }; 239 | D74AD965195171E7009A2ACD = { 240 | isa = PBXGroup; 241 | children = ( 242 | D7AA544125170E1100299291 /* Package.swift */, 243 | D72CE5F420863C9D005BBDEF /* README.md */, 244 | D74AD98B19517201009A2ACD /* LTMorphingLabel */, 245 | D74AD970195171E7009A2ACD /* LTMorphingLabelDemo */, 246 | D7D4B0201A5E87D50087F83D /* LTMorphingLabel */, 247 | D7934DC71A63AF1100AE04CE /* LTMorphingLabelTests */, 248 | D73DFD211F5056A700F84122 /* LTMorphingLabelUITests */, 249 | D74AD96F195171E7009A2ACD /* Products */, 250 | D72A94DF1F7CAF8B007E5157 /* Frameworks */, 251 | ); 252 | sourceTree = ""; 253 | }; 254 | D74AD96F195171E7009A2ACD /* Products */ = { 255 | isa = PBXGroup; 256 | children = ( 257 | D74AD96E195171E7009A2ACD /* Morphing.app */, 258 | D7D4B01F1A5E87D50087F83D /* MorphingLabel.framework */, 259 | D7934DC61A63AF1100AE04CE /* LTMorphingLabelTests.xctest */, 260 | D73DFD1A1F4DB5CC00F84122 /* MorphingLabel.framework */, 261 | D73DFD201F5056A700F84122 /* LTMorphingLabelUITests.xctest */, 262 | ); 263 | name = Products; 264 | sourceTree = ""; 265 | }; 266 | D74AD970195171E7009A2ACD /* LTMorphingLabelDemo */ = { 267 | isa = PBXGroup; 268 | children = ( 269 | D7E537311B8CADF6008EF6D1 /* Launch Screen.storyboard */, 270 | D74AD973195171E7009A2ACD /* AppDelegate.swift */, 271 | D74AD975195171E7009A2ACD /* Images.xcassets */, 272 | D74AD971195171E7009A2ACD /* Supporting Files */, 273 | D74AD98C195173B5009A2ACD /* LTMorphingLabelDemo.storyboard */, 274 | D7633A2A195812020009D084 /* LTDemoViewController.swift */, 275 | ); 276 | path = LTMorphingLabelDemo; 277 | sourceTree = ""; 278 | }; 279 | D74AD971195171E7009A2ACD /* Supporting Files */ = { 280 | isa = PBXGroup; 281 | children = ( 282 | D74AD972195171E7009A2ACD /* Info.plist */, 283 | ); 284 | name = "Supporting Files"; 285 | sourceTree = ""; 286 | }; 287 | D74AD98B19517201009A2ACD /* LTMorphingLabel */ = { 288 | isa = PBXGroup; 289 | children = ( 290 | D7AFEC911962511C0006FA88 /* LTEasing.swift */, 291 | D797F6F81D2E992200531960 /* LTCharacterDiffResult.swift */, 292 | D7633A2C195965A00009D084 /* LTStringDiffResult.swift */, 293 | D7EF4F061AB5612C00D46A0E /* LTCharacterLimbo.swift */, 294 | D7EF4F031AB5608000D46A0E /* LTMorphingEffect.swift */, 295 | D7EF4F091AB561A400D46A0E /* LTEmitterView.swift */, 296 | D74AD98E19517430009A2ACD /* LTMorphingLabel.swift */, 297 | D7AFEC93196256720006FA88 /* LTMorphingLabel+Evaporate.swift */, 298 | D7AFEC97196280860006FA88 /* LTMorphingLabel+Fall.swift */, 299 | D7558D391962AB3000DCF592 /* LTMorphingLabel+Pixelate.swift */, 300 | D7EC5FD5196692E8004E7076 /* LTMorphingLabel+Sparkle.swift */, 301 | D757C30C1974243400A34565 /* LTMorphingLabel+Burn.swift */, 302 | D7C98F381975265C00902C5C /* LTMorphingLabel+Anvil.swift */, 303 | D70FB1FB19685C3300EF9B84 /* Particles */, 304 | D7868570246848E100D4C46F /* SwiftUI */, 305 | ); 306 | path = LTMorphingLabel; 307 | sourceTree = ""; 308 | }; 309 | D7868570246848E100D4C46F /* SwiftUI */ = { 310 | isa = PBXGroup; 311 | children = ( 312 | D764373D2467A01A0037C36B /* MorphingText.swift */, 313 | ); 314 | path = SwiftUI; 315 | sourceTree = ""; 316 | }; 317 | D7934DC71A63AF1100AE04CE /* LTMorphingLabelTests */ = { 318 | isa = PBXGroup; 319 | children = ( 320 | D7934DCA1A63AF1100AE04CE /* LTMorphingLabelTests.swift */, 321 | D7934DC81A63AF1100AE04CE /* Supporting Files */, 322 | ); 323 | path = LTMorphingLabelTests; 324 | sourceTree = ""; 325 | }; 326 | D7934DC81A63AF1100AE04CE /* Supporting Files */ = { 327 | isa = PBXGroup; 328 | children = ( 329 | D7934DC91A63AF1100AE04CE /* Info.plist */, 330 | ); 331 | name = "Supporting Files"; 332 | sourceTree = ""; 333 | }; 334 | D7D4B0201A5E87D50087F83D /* LTMorphingLabel */ = { 335 | isa = PBXGroup; 336 | children = ( 337 | D7D4B0231A5E87D50087F83D /* LTMorphingLabel.h */, 338 | D7D4B0211A5E87D50087F83D /* Supporting Files */, 339 | ); 340 | path = LTMorphingLabel; 341 | sourceTree = ""; 342 | }; 343 | D7D4B0211A5E87D50087F83D /* Supporting Files */ = { 344 | isa = PBXGroup; 345 | children = ( 346 | D7D4B0221A5E87D50087F83D /* Info.plist */, 347 | D73DFD1B1F4DB5CC00F84122 /* tvOS-Info.plist */, 348 | ); 349 | name = "Supporting Files"; 350 | sourceTree = ""; 351 | }; 352 | /* End PBXGroup section */ 353 | 354 | /* Begin PBXHeadersBuildPhase section */ 355 | D73DFD101F4DB5CC00F84122 /* Headers */ = { 356 | isa = PBXHeadersBuildPhase; 357 | buildActionMask = 2147483647; 358 | files = ( 359 | D73DFD111F4DB5CC00F84122 /* LTMorphingLabel.h in Headers */, 360 | ); 361 | runOnlyForDeploymentPostprocessing = 0; 362 | }; 363 | D7D4B01C1A5E87D50087F83D /* Headers */ = { 364 | isa = PBXHeadersBuildPhase; 365 | buildActionMask = 2147483647; 366 | files = ( 367 | D7D4B0241A5E87D50087F83D /* LTMorphingLabel.h in Headers */, 368 | ); 369 | runOnlyForDeploymentPostprocessing = 0; 370 | }; 371 | /* End PBXHeadersBuildPhase section */ 372 | 373 | /* Begin PBXNativeTarget section */ 374 | D73DFD001F4DB5CC00F84122 /* MorphingLabel_tvOS */ = { 375 | isa = PBXNativeTarget; 376 | buildConfigurationList = D73DFD171F4DB5CC00F84122 /* Build configuration list for PBXNativeTarget "MorphingLabel_tvOS" */; 377 | buildPhases = ( 378 | D73DFD011F4DB5CC00F84122 /* Sources */, 379 | D73DFD0F1F4DB5CC00F84122 /* Frameworks */, 380 | D73DFD101F4DB5CC00F84122 /* Headers */, 381 | D73DFD121F4DB5CC00F84122 /* Resources */, 382 | ); 383 | buildRules = ( 384 | ); 385 | dependencies = ( 386 | ); 387 | name = MorphingLabel_tvOS; 388 | productName = LTMorphingLabel; 389 | productReference = D73DFD1A1F4DB5CC00F84122 /* MorphingLabel.framework */; 390 | productType = "com.apple.product-type.framework"; 391 | }; 392 | D73DFD1F1F5056A700F84122 /* LTMorphingLabelUITests */ = { 393 | isa = PBXNativeTarget; 394 | buildConfigurationList = D73DFD291F5056A700F84122 /* Build configuration list for PBXNativeTarget "LTMorphingLabelUITests" */; 395 | buildPhases = ( 396 | D73DFD1C1F5056A700F84122 /* Sources */, 397 | D73DFD1D1F5056A700F84122 /* Frameworks */, 398 | D73DFD1E1F5056A700F84122 /* Resources */, 399 | ); 400 | buildRules = ( 401 | ); 402 | dependencies = ( 403 | D73DFD261F5056A700F84122 /* PBXTargetDependency */, 404 | D72A94E31F7CAFAA007E5157 /* PBXTargetDependency */, 405 | ); 406 | name = LTMorphingLabelUITests; 407 | productName = LTMorphingLabelUITests; 408 | productReference = D73DFD201F5056A700F84122 /* LTMorphingLabelUITests.xctest */; 409 | productType = "com.apple.product-type.bundle.ui-testing"; 410 | }; 411 | D74AD96D195171E7009A2ACD /* LTMorphingLabelDemo */ = { 412 | isa = PBXNativeTarget; 413 | buildConfigurationList = D74AD985195171E7009A2ACD /* Build configuration list for PBXNativeTarget "LTMorphingLabelDemo" */; 414 | buildPhases = ( 415 | D74AD96A195171E7009A2ACD /* Sources */, 416 | D74AD96B195171E7009A2ACD /* Frameworks */, 417 | D74AD96C195171E7009A2ACD /* Resources */, 418 | D7D4B0371A5E87D50087F83D /* Embed Frameworks */, 419 | D7E30A621C28449F00A708CF /* SwiftLint */, 420 | ); 421 | buildRules = ( 422 | ); 423 | dependencies = ( 424 | D72A94DE1F7CAF6D007E5157 /* PBXTargetDependency */, 425 | ); 426 | name = LTMorphingLabelDemo; 427 | productName = LTMorphingLabelDemo; 428 | productReference = D74AD96E195171E7009A2ACD /* Morphing.app */; 429 | productType = "com.apple.product-type.application"; 430 | }; 431 | D7934DC51A63AF1100AE04CE /* LTMorphingLabelTests */ = { 432 | isa = PBXNativeTarget; 433 | buildConfigurationList = D7934DD11A63AF1100AE04CE /* Build configuration list for PBXNativeTarget "LTMorphingLabelTests" */; 434 | buildPhases = ( 435 | D7934DC21A63AF1100AE04CE /* Sources */, 436 | D7934DC31A63AF1100AE04CE /* Frameworks */, 437 | D7934DC41A63AF1100AE04CE /* Resources */, 438 | ); 439 | buildRules = ( 440 | ); 441 | dependencies = ( 442 | D73C19F91AC2823900C1A824 /* PBXTargetDependency */, 443 | D73C19FC1AC28DAA00C1A824 /* PBXTargetDependency */, 444 | ); 445 | name = LTMorphingLabelTests; 446 | productName = LTMorphingLabelTests; 447 | productReference = D7934DC61A63AF1100AE04CE /* LTMorphingLabelTests.xctest */; 448 | productType = "com.apple.product-type.bundle.unit-test"; 449 | }; 450 | D7D4B01E1A5E87D50087F83D /* MorphingLabel */ = { 451 | isa = PBXNativeTarget; 452 | buildConfigurationList = D7D4B03D1A5E87D50087F83D /* Build configuration list for PBXNativeTarget "MorphingLabel" */; 453 | buildPhases = ( 454 | D7D4B01A1A5E87D50087F83D /* Sources */, 455 | D7D4B01B1A5E87D50087F83D /* Frameworks */, 456 | D7D4B01C1A5E87D50087F83D /* Headers */, 457 | D7D4B01D1A5E87D50087F83D /* Resources */, 458 | ); 459 | buildRules = ( 460 | ); 461 | dependencies = ( 462 | ); 463 | name = MorphingLabel; 464 | productName = LTMorphingLabel; 465 | productReference = D7D4B01F1A5E87D50087F83D /* MorphingLabel.framework */; 466 | productType = "com.apple.product-type.framework"; 467 | }; 468 | /* End PBXNativeTarget section */ 469 | 470 | /* Begin PBXProject section */ 471 | D74AD966195171E7009A2ACD /* Project object */ = { 472 | isa = PBXProject; 473 | attributes = { 474 | LastSwiftMigration = 0700; 475 | LastSwiftUpdateCheck = 0830; 476 | LastUpgradeCheck = 1240; 477 | ORGANIZATIONNAME = lexrus.com; 478 | TargetAttributes = { 479 | D73DFD1F1F5056A700F84122 = { 480 | CreatedOnToolsVersion = 8.3.3; 481 | DevelopmentTeam = 5SKD83S59G; 482 | LastSwiftMigration = 1020; 483 | ProvisioningStyle = Automatic; 484 | TestTargetID = D74AD96D195171E7009A2ACD; 485 | }; 486 | D74AD96D195171E7009A2ACD = { 487 | CreatedOnToolsVersion = 6.0; 488 | LastSwiftMigration = 1020; 489 | }; 490 | D7934DC51A63AF1100AE04CE = { 491 | CreatedOnToolsVersion = 6.1.1; 492 | LastSwiftMigration = 1020; 493 | TestTargetID = D74AD96D195171E7009A2ACD; 494 | }; 495 | D7D4B01E1A5E87D50087F83D = { 496 | CreatedOnToolsVersion = 6.1.1; 497 | LastSwiftMigration = 1020; 498 | }; 499 | }; 500 | }; 501 | buildConfigurationList = D74AD969195171E7009A2ACD /* Build configuration list for PBXProject "LTMorphingLabelDemo" */; 502 | compatibilityVersion = "Xcode 6.3"; 503 | developmentRegion = en; 504 | hasScannedForEncodings = 0; 505 | knownRegions = ( 506 | en, 507 | Base, 508 | ); 509 | mainGroup = D74AD965195171E7009A2ACD; 510 | productRefGroup = D74AD96F195171E7009A2ACD /* Products */; 511 | projectDirPath = ""; 512 | projectRoot = ""; 513 | targets = ( 514 | D74AD96D195171E7009A2ACD /* LTMorphingLabelDemo */, 515 | D7D4B01E1A5E87D50087F83D /* MorphingLabel */, 516 | D7934DC51A63AF1100AE04CE /* LTMorphingLabelTests */, 517 | D73DFD001F4DB5CC00F84122 /* MorphingLabel_tvOS */, 518 | D73DFD1F1F5056A700F84122 /* LTMorphingLabelUITests */, 519 | ); 520 | }; 521 | /* End PBXProject section */ 522 | 523 | /* Begin PBXResourcesBuildPhase section */ 524 | D73DFD121F4DB5CC00F84122 /* Resources */ = { 525 | isa = PBXResourcesBuildPhase; 526 | buildActionMask = 2147483647; 527 | files = ( 528 | D73DFD131F4DB5CC00F84122 /* Fire.png in Resources */, 529 | D73DFD141F4DB5CC00F84122 /* Sparkle.png in Resources */, 530 | D73DFD151F4DB5CC00F84122 /* Smoke.png in Resources */, 531 | D73DFD161F4DB5CC00F84122 /* Fragment.png in Resources */, 532 | ); 533 | runOnlyForDeploymentPostprocessing = 0; 534 | }; 535 | D73DFD1E1F5056A700F84122 /* Resources */ = { 536 | isa = PBXResourcesBuildPhase; 537 | buildActionMask = 2147483647; 538 | files = ( 539 | ); 540 | runOnlyForDeploymentPostprocessing = 0; 541 | }; 542 | D74AD96C195171E7009A2ACD /* Resources */ = { 543 | isa = PBXResourcesBuildPhase; 544 | buildActionMask = 2147483647; 545 | files = ( 546 | D720D07D1A5E923800747465 /* Fire.png in Resources */, 547 | D720D07E1A5E923800747465 /* Sparkle.png in Resources */, 548 | D720D07F1A5E923800747465 /* Smoke.png in Resources */, 549 | D720D0801A5E923800747465 /* Fragment.png in Resources */, 550 | D74AD98D195173B5009A2ACD /* LTMorphingLabelDemo.storyboard in Resources */, 551 | D74AD976195171E7009A2ACD /* Images.xcassets in Resources */, 552 | D7E537321B8CADF6008EF6D1 /* Launch Screen.storyboard in Resources */, 553 | ); 554 | runOnlyForDeploymentPostprocessing = 0; 555 | }; 556 | D7934DC41A63AF1100AE04CE /* Resources */ = { 557 | isa = PBXResourcesBuildPhase; 558 | buildActionMask = 2147483647; 559 | files = ( 560 | ); 561 | runOnlyForDeploymentPostprocessing = 0; 562 | }; 563 | D7D4B01D1A5E87D50087F83D /* Resources */ = { 564 | isa = PBXResourcesBuildPhase; 565 | buildActionMask = 2147483647; 566 | files = ( 567 | D7D4B03F1A5E8F6E0087F83D /* Fire.png in Resources */, 568 | D7D4B0401A5E8F6E0087F83D /* Sparkle.png in Resources */, 569 | D7D4B0411A5E8F6E0087F83D /* Smoke.png in Resources */, 570 | D7D4B0421A5E8F6E0087F83D /* Fragment.png in Resources */, 571 | ); 572 | runOnlyForDeploymentPostprocessing = 0; 573 | }; 574 | /* End PBXResourcesBuildPhase section */ 575 | 576 | /* Begin PBXShellScriptBuildPhase section */ 577 | D7E30A621C28449F00A708CF /* SwiftLint */ = { 578 | isa = PBXShellScriptBuildPhase; 579 | buildActionMask = 2147483647; 580 | files = ( 581 | ); 582 | inputPaths = ( 583 | ); 584 | name = SwiftLint; 585 | outputPaths = ( 586 | ); 587 | runOnlyForDeploymentPostprocessing = 0; 588 | shellPath = /bin/sh; 589 | shellScript = "if which swiftlint >/dev/null; then\nswiftlint\nelse\necho \"SwiftLint does not exist, download from https://github.com/realm/SwiftLint\"\nfi\n"; 590 | }; 591 | /* End PBXShellScriptBuildPhase section */ 592 | 593 | /* Begin PBXSourcesBuildPhase section */ 594 | D73DFD011F4DB5CC00F84122 /* Sources */ = { 595 | isa = PBXSourcesBuildPhase; 596 | buildActionMask = 2147483647; 597 | files = ( 598 | D73DFD021F4DB5CC00F84122 /* LTCharacterLimbo.swift in Sources */, 599 | D73DFD031F4DB5CC00F84122 /* LTEasing.swift in Sources */, 600 | D73DFD041F4DB5CC00F84122 /* LTStringDiffResult.swift in Sources */, 601 | D73DFD051F4DB5CC00F84122 /* LTMorphingEffect.swift in Sources */, 602 | D73DFD061F4DB5CC00F84122 /* LTMorphingLabel.swift in Sources */, 603 | D73DFD071F4DB5CC00F84122 /* LTCharacterDiffResult.swift in Sources */, 604 | D73DFD081F4DB5CC00F84122 /* LTMorphingLabel+Evaporate.swift in Sources */, 605 | D73DFD091F4DB5CC00F84122 /* LTMorphingLabel+Fall.swift in Sources */, 606 | D73DFD0A1F4DB5CC00F84122 /* LTMorphingLabel+Pixelate.swift in Sources */, 607 | D73DFD0B1F4DB5CC00F84122 /* LTMorphingLabel+Sparkle.swift in Sources */, 608 | D73DFD0C1F4DB5CC00F84122 /* LTMorphingLabel+Burn.swift in Sources */, 609 | D73DFD0D1F4DB5CC00F84122 /* LTMorphingLabel+Anvil.swift in Sources */, 610 | D73DFD0E1F4DB5CC00F84122 /* LTEmitterView.swift in Sources */, 611 | ); 612 | runOnlyForDeploymentPostprocessing = 0; 613 | }; 614 | D73DFD1C1F5056A700F84122 /* Sources */ = { 615 | isa = PBXSourcesBuildPhase; 616 | buildActionMask = 2147483647; 617 | files = ( 618 | D73DFD231F5056A700F84122 /* LTMorphingLabelUITests.swift in Sources */, 619 | ); 620 | runOnlyForDeploymentPostprocessing = 0; 621 | }; 622 | D74AD96A195171E7009A2ACD /* Sources */ = { 623 | isa = PBXSourcesBuildPhase; 624 | buildActionMask = 2147483647; 625 | files = ( 626 | D720D0741A5E923100747465 /* LTEasing.swift in Sources */, 627 | D7EF4F071AB5612C00D46A0E /* LTCharacterLimbo.swift in Sources */, 628 | D720D0751A5E923100747465 /* LTStringDiffResult.swift in Sources */, 629 | D7EF4F051AB5610200D46A0E /* LTMorphingEffect.swift in Sources */, 630 | D720D0761A5E923100747465 /* LTMorphingLabel.swift in Sources */, 631 | D720D0771A5E923100747465 /* LTMorphingLabel+Evaporate.swift in Sources */, 632 | D720D0781A5E923100747465 /* LTMorphingLabel+Fall.swift in Sources */, 633 | D720D0791A5E923100747465 /* LTMorphingLabel+Pixelate.swift in Sources */, 634 | D720D07A1A5E923100747465 /* LTMorphingLabel+Sparkle.swift in Sources */, 635 | D720D07B1A5E923100747465 /* LTMorphingLabel+Burn.swift in Sources */, 636 | D720D07C1A5E923100747465 /* LTMorphingLabel+Anvil.swift in Sources */, 637 | D7EF4F0A1AB561A400D46A0E /* LTEmitterView.swift in Sources */, 638 | D74AD974195171E7009A2ACD /* AppDelegate.swift in Sources */, 639 | D797F6FA1D2EA47A00531960 /* LTCharacterDiffResult.swift in Sources */, 640 | D7633A2B195812020009D084 /* LTDemoViewController.swift in Sources */, 641 | ); 642 | runOnlyForDeploymentPostprocessing = 0; 643 | }; 644 | D7934DC21A63AF1100AE04CE /* Sources */ = { 645 | isa = PBXSourcesBuildPhase; 646 | buildActionMask = 2147483647; 647 | files = ( 648 | D7934DCB1A63AF1100AE04CE /* LTMorphingLabelTests.swift in Sources */, 649 | ); 650 | runOnlyForDeploymentPostprocessing = 0; 651 | }; 652 | D7D4B01A1A5E87D50087F83D /* Sources */ = { 653 | isa = PBXSourcesBuildPhase; 654 | buildActionMask = 2147483647; 655 | files = ( 656 | D701040D2516F5E500DB98F7 /* MorphingText.swift in Sources */, 657 | D7EF4F081AB5612C00D46A0E /* LTCharacterLimbo.swift in Sources */, 658 | D720D06B1A5E91CB00747465 /* LTEasing.swift in Sources */, 659 | D720D06C1A5E91CB00747465 /* LTStringDiffResult.swift in Sources */, 660 | D7EF4F041AB5608000D46A0E /* LTMorphingEffect.swift in Sources */, 661 | D720D06D1A5E91CB00747465 /* LTMorphingLabel.swift in Sources */, 662 | D797F6F91D2E992200531960 /* LTCharacterDiffResult.swift in Sources */, 663 | D720D06E1A5E91CB00747465 /* LTMorphingLabel+Evaporate.swift in Sources */, 664 | D720D06F1A5E91CB00747465 /* LTMorphingLabel+Fall.swift in Sources */, 665 | D720D0701A5E91CB00747465 /* LTMorphingLabel+Pixelate.swift in Sources */, 666 | D720D0711A5E91CB00747465 /* LTMorphingLabel+Sparkle.swift in Sources */, 667 | D720D0721A5E91CB00747465 /* LTMorphingLabel+Burn.swift in Sources */, 668 | D720D0731A5E91CB00747465 /* LTMorphingLabel+Anvil.swift in Sources */, 669 | D7EF4F0B1AB561A400D46A0E /* LTEmitterView.swift in Sources */, 670 | ); 671 | runOnlyForDeploymentPostprocessing = 0; 672 | }; 673 | /* End PBXSourcesBuildPhase section */ 674 | 675 | /* Begin PBXTargetDependency section */ 676 | D72A94DE1F7CAF6D007E5157 /* PBXTargetDependency */ = { 677 | isa = PBXTargetDependency; 678 | target = D7D4B01E1A5E87D50087F83D /* MorphingLabel */; 679 | targetProxy = D72A94DD1F7CAF6D007E5157 /* PBXContainerItemProxy */; 680 | }; 681 | D72A94E31F7CAFAA007E5157 /* PBXTargetDependency */ = { 682 | isa = PBXTargetDependency; 683 | target = D7D4B01E1A5E87D50087F83D /* MorphingLabel */; 684 | targetProxy = D72A94E21F7CAFAA007E5157 /* PBXContainerItemProxy */; 685 | }; 686 | D73C19F91AC2823900C1A824 /* PBXTargetDependency */ = { 687 | isa = PBXTargetDependency; 688 | target = D7D4B01E1A5E87D50087F83D /* MorphingLabel */; 689 | targetProxy = D73C19F81AC2823900C1A824 /* PBXContainerItemProxy */; 690 | }; 691 | D73C19FC1AC28DAA00C1A824 /* PBXTargetDependency */ = { 692 | isa = PBXTargetDependency; 693 | target = D74AD96D195171E7009A2ACD /* LTMorphingLabelDemo */; 694 | targetProxy = D73C19FB1AC28DAA00C1A824 /* PBXContainerItemProxy */; 695 | }; 696 | D73DFD261F5056A700F84122 /* PBXTargetDependency */ = { 697 | isa = PBXTargetDependency; 698 | target = D74AD96D195171E7009A2ACD /* LTMorphingLabelDemo */; 699 | targetProxy = D73DFD251F5056A700F84122 /* PBXContainerItemProxy */; 700 | }; 701 | /* End PBXTargetDependency section */ 702 | 703 | /* Begin XCBuildConfiguration section */ 704 | D73DFD181F4DB5CC00F84122 /* Debug */ = { 705 | isa = XCBuildConfiguration; 706 | buildSettings = { 707 | APPLICATION_EXTENSION_API_ONLY = YES; 708 | "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; 709 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 710 | CURRENT_PROJECT_VERSION = 1; 711 | DEFINES_MODULE = YES; 712 | DYLIB_COMPATIBILITY_VERSION = 1; 713 | DYLIB_CURRENT_VERSION = 1; 714 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 715 | GCC_PREPROCESSOR_DEFINITIONS = ( 716 | "DEBUG=1", 717 | "$(inherited)", 718 | ); 719 | INFOPLIST_FILE = "$(SRCROOT)/LTMorphingLabel/tvOS-Info.plist"; 720 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 721 | LD_RUNPATH_SEARCH_PATHS = ( 722 | "$(inherited)", 723 | "@executable_path/Frameworks", 724 | "@loader_path/Frameworks", 725 | ); 726 | MTL_ENABLE_DEBUG_INFO = YES; 727 | PRODUCT_BUNDLE_IDENTIFIER = sh.lex.LTMorphingLabel; 728 | PRODUCT_NAME = MorphingLabel; 729 | SDKROOT = appletvos; 730 | SKIP_INSTALL = YES; 731 | TARGETED_DEVICE_FAMILY = "1,2,3"; 732 | TVOS_DEPLOYMENT_TARGET = 12.0; 733 | VERSIONING_SYSTEM = "apple-generic"; 734 | VERSION_INFO_PREFIX = ""; 735 | }; 736 | name = Debug; 737 | }; 738 | D73DFD191F4DB5CC00F84122 /* Release */ = { 739 | isa = XCBuildConfiguration; 740 | buildSettings = { 741 | APPLICATION_EXTENSION_API_ONLY = YES; 742 | "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; 743 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 744 | CURRENT_PROJECT_VERSION = 1; 745 | DEFINES_MODULE = YES; 746 | DYLIB_COMPATIBILITY_VERSION = 1; 747 | DYLIB_CURRENT_VERSION = 1; 748 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 749 | INFOPLIST_FILE = "$(SRCROOT)/LTMorphingLabel/tvOS-Info.plist"; 750 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 751 | LD_RUNPATH_SEARCH_PATHS = ( 752 | "$(inherited)", 753 | "@executable_path/Frameworks", 754 | "@loader_path/Frameworks", 755 | ); 756 | MTL_ENABLE_DEBUG_INFO = NO; 757 | PRODUCT_BUNDLE_IDENTIFIER = sh.lex.LTMorphingLabel; 758 | PRODUCT_NAME = MorphingLabel; 759 | SDKROOT = appletvos; 760 | SKIP_INSTALL = YES; 761 | TARGETED_DEVICE_FAMILY = "1,2,3"; 762 | TVOS_DEPLOYMENT_TARGET = 12.0; 763 | VERSIONING_SYSTEM = "apple-generic"; 764 | VERSION_INFO_PREFIX = ""; 765 | }; 766 | name = Release; 767 | }; 768 | D73DFD271F5056A700F84122 /* Debug */ = { 769 | isa = XCBuildConfiguration; 770 | buildSettings = { 771 | CLANG_ANALYZER_NONNULL = YES; 772 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 773 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 774 | DEBUG_INFORMATION_FORMAT = dwarf; 775 | DEVELOPMENT_TEAM = 5SKD83S59G; 776 | INFOPLIST_FILE = LTMorphingLabelUITests/Info.plist; 777 | LD_RUNPATH_SEARCH_PATHS = ( 778 | "$(inherited)", 779 | "@executable_path/Frameworks", 780 | "@loader_path/Frameworks", 781 | ); 782 | MTL_ENABLE_DEBUG_INFO = YES; 783 | PRODUCT_BUNDLE_IDENTIFIER = com.LexTang.LTMorphingLabelUITests; 784 | PRODUCT_NAME = "$(TARGET_NAME)"; 785 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 786 | TEST_TARGET_NAME = LTMorphingLabelDemo; 787 | }; 788 | name = Debug; 789 | }; 790 | D73DFD281F5056A700F84122 /* Release */ = { 791 | isa = XCBuildConfiguration; 792 | buildSettings = { 793 | CLANG_ANALYZER_NONNULL = YES; 794 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 795 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 796 | COPY_PHASE_STRIP = NO; 797 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 798 | DEVELOPMENT_TEAM = 5SKD83S59G; 799 | INFOPLIST_FILE = LTMorphingLabelUITests/Info.plist; 800 | LD_RUNPATH_SEARCH_PATHS = ( 801 | "$(inherited)", 802 | "@executable_path/Frameworks", 803 | "@loader_path/Frameworks", 804 | ); 805 | MTL_ENABLE_DEBUG_INFO = NO; 806 | PRODUCT_BUNDLE_IDENTIFIER = com.LexTang.LTMorphingLabelUITests; 807 | PRODUCT_NAME = "$(TARGET_NAME)"; 808 | TEST_TARGET_NAME = LTMorphingLabelDemo; 809 | }; 810 | name = Release; 811 | }; 812 | D74AD983195171E7009A2ACD /* Debug */ = { 813 | isa = XCBuildConfiguration; 814 | buildSettings = { 815 | ALWAYS_SEARCH_USER_PATHS = NO; 816 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 817 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 818 | CLANG_CXX_LIBRARY = "libc++"; 819 | CLANG_ENABLE_MODULES = YES; 820 | CLANG_ENABLE_OBJC_ARC = YES; 821 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 822 | CLANG_WARN_BOOL_CONVERSION = YES; 823 | CLANG_WARN_COMMA = YES; 824 | CLANG_WARN_CONSTANT_CONVERSION = YES; 825 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 826 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 827 | CLANG_WARN_EMPTY_BODY = YES; 828 | CLANG_WARN_ENUM_CONVERSION = YES; 829 | CLANG_WARN_INFINITE_RECURSION = YES; 830 | CLANG_WARN_INT_CONVERSION = YES; 831 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 832 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 833 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 834 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 835 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 836 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 837 | CLANG_WARN_STRICT_PROTOTYPES = YES; 838 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 839 | CLANG_WARN_UNREACHABLE_CODE = YES; 840 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 841 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 842 | COPY_PHASE_STRIP = NO; 843 | ENABLE_STRICT_OBJC_MSGSEND = YES; 844 | ENABLE_TESTABILITY = YES; 845 | GCC_C_LANGUAGE_STANDARD = gnu99; 846 | GCC_DYNAMIC_NO_PIC = NO; 847 | GCC_NO_COMMON_BLOCKS = YES; 848 | GCC_OPTIMIZATION_LEVEL = 0; 849 | GCC_PREPROCESSOR_DEFINITIONS = ( 850 | "DEBUG=1", 851 | "$(inherited)", 852 | ); 853 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 854 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 855 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 856 | GCC_WARN_UNDECLARED_SELECTOR = YES; 857 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 858 | GCC_WARN_UNUSED_FUNCTION = YES; 859 | GCC_WARN_UNUSED_VARIABLE = YES; 860 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 861 | METAL_ENABLE_DEBUG_INFO = YES; 862 | ONLY_ACTIVE_ARCH = YES; 863 | SDKROOT = iphoneos; 864 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 865 | SWIFT_VERSION = 5.0; 866 | }; 867 | name = Debug; 868 | }; 869 | D74AD984195171E7009A2ACD /* Release */ = { 870 | isa = XCBuildConfiguration; 871 | buildSettings = { 872 | ALWAYS_SEARCH_USER_PATHS = NO; 873 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 874 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 875 | CLANG_CXX_LIBRARY = "libc++"; 876 | CLANG_ENABLE_MODULES = YES; 877 | CLANG_ENABLE_OBJC_ARC = YES; 878 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 879 | CLANG_WARN_BOOL_CONVERSION = YES; 880 | CLANG_WARN_COMMA = YES; 881 | CLANG_WARN_CONSTANT_CONVERSION = YES; 882 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 883 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 884 | CLANG_WARN_EMPTY_BODY = YES; 885 | CLANG_WARN_ENUM_CONVERSION = YES; 886 | CLANG_WARN_INFINITE_RECURSION = YES; 887 | CLANG_WARN_INT_CONVERSION = YES; 888 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 889 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 890 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 891 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 892 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 893 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 894 | CLANG_WARN_STRICT_PROTOTYPES = YES; 895 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 896 | CLANG_WARN_UNREACHABLE_CODE = YES; 897 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 898 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 899 | COPY_PHASE_STRIP = YES; 900 | ENABLE_NS_ASSERTIONS = NO; 901 | ENABLE_STRICT_OBJC_MSGSEND = YES; 902 | GCC_C_LANGUAGE_STANDARD = gnu99; 903 | GCC_NO_COMMON_BLOCKS = YES; 904 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 905 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 906 | GCC_WARN_UNDECLARED_SELECTOR = YES; 907 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 908 | GCC_WARN_UNUSED_FUNCTION = YES; 909 | GCC_WARN_UNUSED_VARIABLE = YES; 910 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 911 | METAL_ENABLE_DEBUG_INFO = NO; 912 | SDKROOT = iphoneos; 913 | SWIFT_COMPILATION_MODE = wholemodule; 914 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 915 | SWIFT_VERSION = 5.0; 916 | VALIDATE_PRODUCT = YES; 917 | }; 918 | name = Release; 919 | }; 920 | D74AD986195171E7009A2ACD /* Debug */ = { 921 | isa = XCBuildConfiguration; 922 | buildSettings = { 923 | ALWAYS_SEARCH_USER_PATHS = NO; 924 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 925 | CLANG_ENABLE_MODULES = YES; 926 | CODE_SIGN_IDENTITY = "iPhone Developer"; 927 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 928 | DEVELOPMENT_TEAM = 5SKD83S59G; 929 | INFOPLIST_FILE = LTMorphingLabelDemo/Info.plist; 930 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 931 | LD_RUNPATH_SEARCH_PATHS = ( 932 | "$(inherited)", 933 | "@executable_path/Frameworks", 934 | ); 935 | PRODUCT_BUNDLE_IDENTIFIER = sh.lex.LTMorphingLabelDemo; 936 | PRODUCT_NAME = Morphing; 937 | PROVISIONING_PROFILE = ""; 938 | SWIFT_INCLUDE_PATHS = ""; 939 | SWIFT_OBJC_BRIDGING_HEADER = ""; 940 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 941 | TARGETED_DEVICE_FAMILY = "1,2"; 942 | }; 943 | name = Debug; 944 | }; 945 | D74AD987195171E7009A2ACD /* Release */ = { 946 | isa = XCBuildConfiguration; 947 | buildSettings = { 948 | ALWAYS_SEARCH_USER_PATHS = NO; 949 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 950 | CLANG_ENABLE_MODULES = YES; 951 | CODE_SIGN_IDENTITY = "iPhone Developer"; 952 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 953 | DEVELOPMENT_TEAM = 5SKD83S59G; 954 | INFOPLIST_FILE = LTMorphingLabelDemo/Info.plist; 955 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 956 | LD_RUNPATH_SEARCH_PATHS = ( 957 | "$(inherited)", 958 | "@executable_path/Frameworks", 959 | ); 960 | PRODUCT_BUNDLE_IDENTIFIER = sh.lex.LTMorphingLabelDemo; 961 | PRODUCT_NAME = Morphing; 962 | PROVISIONING_PROFILE = ""; 963 | SWIFT_INCLUDE_PATHS = ""; 964 | SWIFT_OBJC_BRIDGING_HEADER = ""; 965 | TARGETED_DEVICE_FAMILY = "1,2"; 966 | }; 967 | name = Release; 968 | }; 969 | D7934DCF1A63AF1100AE04CE /* Debug */ = { 970 | isa = XCBuildConfiguration; 971 | buildSettings = { 972 | GCC_PREPROCESSOR_DEFINITIONS = ( 973 | "DEBUG=1", 974 | "$(inherited)", 975 | ); 976 | INFOPLIST_FILE = LTMorphingLabelTests/Info.plist; 977 | LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; 978 | MTL_ENABLE_DEBUG_INFO = YES; 979 | PRODUCT_BUNDLE_IDENTIFIER = "com.LexTang.$(PRODUCT_NAME:rfc1034identifier)"; 980 | PRODUCT_NAME = "$(TARGET_NAME)"; 981 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/LTMorphingLabelDemo.app/LTMorphingLabelDemo"; 982 | }; 983 | name = Debug; 984 | }; 985 | D7934DD01A63AF1100AE04CE /* Release */ = { 986 | isa = XCBuildConfiguration; 987 | buildSettings = { 988 | INFOPLIST_FILE = LTMorphingLabelTests/Info.plist; 989 | LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; 990 | MTL_ENABLE_DEBUG_INFO = NO; 991 | PRODUCT_BUNDLE_IDENTIFIER = "com.LexTang.$(PRODUCT_NAME:rfc1034identifier)"; 992 | PRODUCT_NAME = "$(TARGET_NAME)"; 993 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/LTMorphingLabelDemo.app/LTMorphingLabelDemo"; 994 | }; 995 | name = Release; 996 | }; 997 | D7D4B0391A5E87D50087F83D /* Debug */ = { 998 | isa = XCBuildConfiguration; 999 | buildSettings = { 1000 | APPLICATION_EXTENSION_API_ONLY = YES; 1001 | CODE_SIGN_IDENTITY = ""; 1002 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 1003 | "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; 1004 | CODE_SIGN_STYLE = Manual; 1005 | CURRENT_PROJECT_VERSION = 1; 1006 | DEFINES_MODULE = YES; 1007 | DEVELOPMENT_TEAM = ""; 1008 | DYLIB_COMPATIBILITY_VERSION = 1; 1009 | DYLIB_CURRENT_VERSION = 1; 1010 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 1011 | GCC_PREPROCESSOR_DEFINITIONS = ( 1012 | "DEBUG=1", 1013 | "$(inherited)", 1014 | ); 1015 | INFOPLIST_FILE = LTMorphingLabel/Info.plist; 1016 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 1017 | LD_RUNPATH_SEARCH_PATHS = ( 1018 | "$(inherited)", 1019 | "@executable_path/Frameworks", 1020 | "@loader_path/Frameworks", 1021 | ); 1022 | MTL_ENABLE_DEBUG_INFO = YES; 1023 | PRODUCT_BUNDLE_IDENTIFIER = sh.lex.LTMorphingLabel; 1024 | PRODUCT_NAME = MorphingLabel; 1025 | PROVISIONING_PROFILE_SPECIFIER = ""; 1026 | "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = ""; 1027 | SKIP_INSTALL = YES; 1028 | TARGETED_DEVICE_FAMILY = "1,2"; 1029 | VERSIONING_SYSTEM = "apple-generic"; 1030 | VERSION_INFO_PREFIX = ""; 1031 | }; 1032 | name = Debug; 1033 | }; 1034 | D7D4B03A1A5E87D50087F83D /* Release */ = { 1035 | isa = XCBuildConfiguration; 1036 | buildSettings = { 1037 | APPLICATION_EXTENSION_API_ONLY = YES; 1038 | CODE_SIGN_IDENTITY = ""; 1039 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 1040 | "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; 1041 | CODE_SIGN_STYLE = Manual; 1042 | CURRENT_PROJECT_VERSION = 1; 1043 | DEFINES_MODULE = YES; 1044 | DEVELOPMENT_TEAM = ""; 1045 | DYLIB_COMPATIBILITY_VERSION = 1; 1046 | DYLIB_CURRENT_VERSION = 1; 1047 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 1048 | INFOPLIST_FILE = LTMorphingLabel/Info.plist; 1049 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 1050 | LD_RUNPATH_SEARCH_PATHS = ( 1051 | "$(inherited)", 1052 | "@executable_path/Frameworks", 1053 | "@loader_path/Frameworks", 1054 | ); 1055 | MTL_ENABLE_DEBUG_INFO = NO; 1056 | PRODUCT_BUNDLE_IDENTIFIER = sh.lex.LTMorphingLabel; 1057 | PRODUCT_NAME = MorphingLabel; 1058 | PROVISIONING_PROFILE_SPECIFIER = ""; 1059 | "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = ""; 1060 | SKIP_INSTALL = YES; 1061 | TARGETED_DEVICE_FAMILY = "1,2"; 1062 | VERSIONING_SYSTEM = "apple-generic"; 1063 | VERSION_INFO_PREFIX = ""; 1064 | }; 1065 | name = Release; 1066 | }; 1067 | /* End XCBuildConfiguration section */ 1068 | 1069 | /* Begin XCConfigurationList section */ 1070 | D73DFD171F4DB5CC00F84122 /* Build configuration list for PBXNativeTarget "MorphingLabel_tvOS" */ = { 1071 | isa = XCConfigurationList; 1072 | buildConfigurations = ( 1073 | D73DFD181F4DB5CC00F84122 /* Debug */, 1074 | D73DFD191F4DB5CC00F84122 /* Release */, 1075 | ); 1076 | defaultConfigurationIsVisible = 0; 1077 | defaultConfigurationName = Release; 1078 | }; 1079 | D73DFD291F5056A700F84122 /* Build configuration list for PBXNativeTarget "LTMorphingLabelUITests" */ = { 1080 | isa = XCConfigurationList; 1081 | buildConfigurations = ( 1082 | D73DFD271F5056A700F84122 /* Debug */, 1083 | D73DFD281F5056A700F84122 /* Release */, 1084 | ); 1085 | defaultConfigurationIsVisible = 0; 1086 | defaultConfigurationName = Release; 1087 | }; 1088 | D74AD969195171E7009A2ACD /* Build configuration list for PBXProject "LTMorphingLabelDemo" */ = { 1089 | isa = XCConfigurationList; 1090 | buildConfigurations = ( 1091 | D74AD983195171E7009A2ACD /* Debug */, 1092 | D74AD984195171E7009A2ACD /* Release */, 1093 | ); 1094 | defaultConfigurationIsVisible = 0; 1095 | defaultConfigurationName = Release; 1096 | }; 1097 | D74AD985195171E7009A2ACD /* Build configuration list for PBXNativeTarget "LTMorphingLabelDemo" */ = { 1098 | isa = XCConfigurationList; 1099 | buildConfigurations = ( 1100 | D74AD986195171E7009A2ACD /* Debug */, 1101 | D74AD987195171E7009A2ACD /* Release */, 1102 | ); 1103 | defaultConfigurationIsVisible = 0; 1104 | defaultConfigurationName = Release; 1105 | }; 1106 | D7934DD11A63AF1100AE04CE /* Build configuration list for PBXNativeTarget "LTMorphingLabelTests" */ = { 1107 | isa = XCConfigurationList; 1108 | buildConfigurations = ( 1109 | D7934DCF1A63AF1100AE04CE /* Debug */, 1110 | D7934DD01A63AF1100AE04CE /* Release */, 1111 | ); 1112 | defaultConfigurationIsVisible = 0; 1113 | defaultConfigurationName = Release; 1114 | }; 1115 | D7D4B03D1A5E87D50087F83D /* Build configuration list for PBXNativeTarget "MorphingLabel" */ = { 1116 | isa = XCConfigurationList; 1117 | buildConfigurations = ( 1118 | D7D4B0391A5E87D50087F83D /* Debug */, 1119 | D7D4B03A1A5E87D50087F83D /* Release */, 1120 | ); 1121 | defaultConfigurationIsVisible = 0; 1122 | defaultConfigurationName = Release; 1123 | }; 1124 | /* End XCConfigurationList section */ 1125 | }; 1126 | rootObject = D74AD966195171E7009A2ACD /* Project object */; 1127 | } 1128 | -------------------------------------------------------------------------------- /LTMorphingLabelDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /LTMorphingLabelDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /LTMorphingLabelDemo.xcodeproj/project.xcworkspace/xcshareddata/LTMorphingLabelDemo.xccheckout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDESourceControlProjectFavoriteDictionaryKey 6 | 7 | IDESourceControlProjectIdentifier 8 | B5A16937-A9B9-4B6F-B635-3E9C1B270E75 9 | IDESourceControlProjectName 10 | LTMorphingLabelDemo 11 | IDESourceControlProjectOriginsDictionary 12 | 13 | D256575CFF167EE4510B644FB2F3602FDD05F90B 14 | https://github.com/lexrus/LTMorphingLabel.git 15 | 16 | IDESourceControlProjectPath 17 | LTMorphingLabelDemo.xcodeproj 18 | IDESourceControlProjectRelativeInstallPathDictionary 19 | 20 | D256575CFF167EE4510B644FB2F3602FDD05F90B 21 | ../.. 22 | 23 | IDESourceControlProjectURL 24 | https://github.com/lexrus/LTMorphingLabel.git 25 | IDESourceControlProjectVersion 26 | 111 27 | IDESourceControlProjectWCCIdentifier 28 | D256575CFF167EE4510B644FB2F3602FDD05F90B 29 | IDESourceControlProjectWCConfigurations 30 | 31 | 32 | IDESourceControlRepositoryExtensionIdentifierKey 33 | public.vcs.git 34 | IDESourceControlWCCIdentifierKey 35 | D256575CFF167EE4510B644FB2F3602FDD05F90B 36 | IDESourceControlWCCName 37 | LTMorphingLabel 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /LTMorphingLabelDemo.xcodeproj/project.xcworkspace/xcuserdata/lex.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lexrus/LTMorphingLabel/4b80cf8b8ce04f15f8fa0581fd87420e09d4408f/LTMorphingLabelDemo.xcodeproj/project.xcworkspace/xcuserdata/lex.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /LTMorphingLabelDemo.xcodeproj/xcshareddata/xcschemes/LTMorphingLabelDemo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 42 | 48 | 49 | 50 | 52 | 58 | 59 | 60 | 61 | 62 | 72 | 74 | 80 | 81 | 82 | 83 | 89 | 91 | 97 | 98 | 99 | 100 | 102 | 103 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /LTMorphingLabelDemo.xcodeproj/xcshareddata/xcschemes/MorphingLabel.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 50 | 51 | 57 | 58 | 59 | 60 | 62 | 63 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /LTMorphingLabelDemo.xcodeproj/xcuserdata/lex.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | LTMorphingLabelDemo.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | LTMorphingLabelDemo.xcscheme_^#shared#^_ 13 | 14 | orderHint 15 | 1 16 | 17 | MorphingLabel.xcscheme_^#shared#^_ 18 | 19 | orderHint 20 | 0 21 | 22 | MorphingLabel_tvOS.xcscheme_^#shared#^_ 23 | 24 | orderHint 25 | 2 26 | 27 | 28 | SuppressBuildableAutocreation 29 | 30 | D73DFD001F4DB5CC00F84122 31 | 32 | primary 33 | 34 | 35 | D73DFD1F1F5056A700F84122 36 | 37 | primary 38 | 39 | 40 | D74AD96D195171E7009A2ACD 41 | 42 | primary 43 | 44 | 45 | D7934DC51A63AF1100AE04CE 46 | 47 | primary 48 | 49 | 50 | D7D4B01E1A5E87D50087F83D 51 | 52 | primary 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /LTMorphingLabelDemo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // LTMorphingLabelDemo 4 | // 5 | // Created by Lex on 6/18/14. 6 | // Copyright (c) 2015 lexrus.com. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate : UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | func application( 17 | _ application: UIApplication, 18 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 19 | ) -> Bool { 20 | window!.backgroundColor = UIColor.black 21 | window!.makeKeyAndVisible() 22 | return true 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /LTMorphingLabelDemo/Images.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 | "size" : "60x60", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-60@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "60x60", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-60@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "idiom" : "ipad", 47 | "size" : "20x20", 48 | "scale" : "1x" 49 | }, 50 | { 51 | "idiom" : "ipad", 52 | "size" : "20x20", 53 | "scale" : "2x" 54 | }, 55 | { 56 | "idiom" : "ipad", 57 | "size" : "29x29", 58 | "scale" : "1x" 59 | }, 60 | { 61 | "idiom" : "ipad", 62 | "size" : "29x29", 63 | "scale" : "2x" 64 | }, 65 | { 66 | "idiom" : "ipad", 67 | "size" : "40x40", 68 | "scale" : "1x" 69 | }, 70 | { 71 | "idiom" : "ipad", 72 | "size" : "40x40", 73 | "scale" : "2x" 74 | }, 75 | { 76 | "size" : "76x76", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-76.png", 79 | "scale" : "1x" 80 | }, 81 | { 82 | "size" : "76x76", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-76@2x.png", 85 | "scale" : "2x" 86 | }, 87 | { 88 | "size" : "83.5x83.5", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-83.5@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "idiom" : "ios-marketing", 95 | "size" : "1024x1024", 96 | "scale" : "1x" 97 | } 98 | ], 99 | "info" : { 100 | "version" : 1, 101 | "author" : "xcode" 102 | } 103 | } -------------------------------------------------------------------------------- /LTMorphingLabelDemo/Images.xcassets/AppIcon.appiconset/Icon-60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lexrus/LTMorphingLabel/4b80cf8b8ce04f15f8fa0581fd87420e09d4408f/LTMorphingLabelDemo/Images.xcassets/AppIcon.appiconset/Icon-60@2x.png -------------------------------------------------------------------------------- /LTMorphingLabelDemo/Images.xcassets/AppIcon.appiconset/Icon-60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lexrus/LTMorphingLabel/4b80cf8b8ce04f15f8fa0581fd87420e09d4408f/LTMorphingLabelDemo/Images.xcassets/AppIcon.appiconset/Icon-60@3x.png -------------------------------------------------------------------------------- /LTMorphingLabelDemo/Images.xcassets/AppIcon.appiconset/Icon-76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lexrus/LTMorphingLabel/4b80cf8b8ce04f15f8fa0581fd87420e09d4408f/LTMorphingLabelDemo/Images.xcassets/AppIcon.appiconset/Icon-76.png -------------------------------------------------------------------------------- /LTMorphingLabelDemo/Images.xcassets/AppIcon.appiconset/Icon-76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lexrus/LTMorphingLabel/4b80cf8b8ce04f15f8fa0581fd87420e09d4408f/LTMorphingLabelDemo/Images.xcassets/AppIcon.appiconset/Icon-76@2x.png -------------------------------------------------------------------------------- /LTMorphingLabelDemo/Images.xcassets/AppIcon.appiconset/Icon-83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lexrus/LTMorphingLabel/4b80cf8b8ce04f15f8fa0581fd87420e09d4408f/LTMorphingLabelDemo/Images.xcassets/AppIcon.appiconset/Icon-83.5@2x.png -------------------------------------------------------------------------------- /LTMorphingLabelDemo/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /LTMorphingLabelDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | $(PRODUCT_NAME) 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSRequiresIPhoneOS 26 | 27 | NSMainNibFile~ipad 28 | LTMorphingLabelDemo-iPad 29 | UILaunchStoryboardName 30 | Launch Screen 31 | UIMainStoryboardFile 32 | LTMorphingLabelDemo 33 | UIRequiredDeviceCapabilities 34 | 35 | armv7 36 | 37 | UIStatusBarStyle 38 | UIStatusBarStyleLightContent 39 | UISupportedInterfaceOrientations 40 | 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /LTMorphingLabelDemo/LTDemoViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LTDemoViewController.swift 3 | // LTMorphingLabelDemo 4 | // 5 | // Created by Lex on 6/23/14. 6 | // Copyright (c) 2015 lexrus.com. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class LTDemoViewController : UIViewController, LTMorphingLabelDelegate { 12 | 13 | fileprivate var i = -1 14 | fileprivate var textArray = [ 15 | "What is design?", 16 | "Design", "Design is not just", "what it looks like", "and feels like.", 17 | "Design", "is how it works.", "- Steve Jobs", 18 | "Older people", "sit down and ask,", "'What is it?'", 19 | "but the boy asks,", "'What can I do with it?'.", "- Steve Jobs", 20 | "One more thing...", "Swift", "Objective-C", "iPhone", "iPad", "Mac Mini", 21 | "MacBook Pro🔥", "Mac Pro⚡️", 22 | "爱老婆", 23 | "नमस्ते दुनिया", 24 | "हिन्दी भाषा", 25 | "$68.98", 26 | "$68.99", 27 | "$69.00", 28 | "$69.01" 29 | ] 30 | fileprivate var text: String { 31 | i = i >= textArray.count - 1 ? 0 : i + 1 32 | return textArray[i] 33 | } 34 | 35 | @IBOutlet weak var effectSegmentControl: UISegmentedControl! 36 | @IBOutlet weak var themeSegmentControl: UISegmentedControl! 37 | @IBOutlet fileprivate var label: LTMorphingLabel! 38 | 39 | override func viewDidLoad() { 40 | super.viewDidLoad() 41 | 42 | label.delegate = self 43 | 44 | [effectSegmentControl, themeSegmentControl].forEach { 45 | $0?.setTitleTextAttributes([.foregroundColor : UIColor.white], for: .normal) 46 | $0?.setTitleTextAttributes([.foregroundColor : UIColor.white], for: .selected) 47 | } 48 | } 49 | 50 | @IBAction func changeText(_ sender: AnyObject) { 51 | label.text = text 52 | if !autoStart.isOn { 53 | label.pause() 54 | progressSlider.value = 0 55 | } 56 | } 57 | 58 | @IBAction func clear(_ sender: Any) { 59 | label.text = nil 60 | } 61 | 62 | @IBOutlet weak var autoStart: UISwitch! 63 | 64 | @IBAction func updateAutoStart(_ sender: Any) { 65 | progressSlider.isHidden = autoStart.isOn 66 | if autoStart.isOn { 67 | label.resume() 68 | } else { 69 | changeText(NSObject()) 70 | } 71 | } 72 | 73 | @IBOutlet weak var progressSlider: UISlider! 74 | 75 | @IBAction func updateProgress(_ sender: Any) { 76 | label.morphingProgress = progressSlider.value / 100 77 | label.setNeedsDisplay() 78 | } 79 | 80 | @IBAction func segmentChanged(_ sender: UISegmentedControl) { 81 | let seg = sender 82 | if let effect = LTMorphingEffect(rawValue: seg.selectedSegmentIndex) { 83 | label.morphingEffect = effect 84 | changeText(sender) 85 | } 86 | } 87 | 88 | @IBAction func toggleLight(_ sender: UISegmentedControl) { 89 | let isNight = Bool(sender.selectedSegmentIndex == 0) 90 | view.backgroundColor = isNight ? UIColor.black : UIColor.white 91 | label.textColor = isNight ? UIColor.white : UIColor.black 92 | } 93 | 94 | @IBAction func changeFontSize(_ sender: UISlider) { 95 | label.font = UIFont(descriptor: label.font.fontDescriptor, size: CGFloat(sender.value)) 96 | label.text = label.text 97 | } 98 | } 99 | 100 | extension LTDemoViewController { 101 | 102 | func morphingDidStart(_ label: LTMorphingLabel) { 103 | 104 | } 105 | 106 | func morphingDidComplete(_ label: LTMorphingLabel) { 107 | 108 | } 109 | 110 | func morphingOnProgress(_ label: LTMorphingLabel, progress: Float) { 111 | 112 | } 113 | 114 | } 115 | -------------------------------------------------------------------------------- /LTMorphingLabelDemo/LTMorphingLabelDemo.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 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | -------------------------------------------------------------------------------- /LTMorphingLabelDemo/Launch Screen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 28 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /LTMorphingLabelTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /LTMorphingLabelTests/LTMorphingLabelTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LTMorphingLabelTests.swift 3 | // LTMorphingLabelTests 4 | // 5 | // Created by Lex Tang on 1/12/15. 6 | // Copyright (c) 2015 lexrus.com. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import MorphingLabel 11 | 12 | class LTMorphingLabelTests : XCTestCase { 13 | 14 | func testStringDiff1() { 15 | let diffResults = "he".diffWith("hello") 16 | XCTAssert( 17 | diffResults.0[0] == .same, 18 | "First character isn't changed." 19 | ) 20 | XCTAssert(diffResults.0[2] == .add, "Third character is added.") 21 | } 22 | 23 | func testStringDiff2() { 24 | let diffResults = "news".diffWith("westen") 25 | if case .moveAndAdd(let offset) = diffResults.0[0] { 26 | XCTAssert( 27 | offset == 5, 28 | "n is moved right for 5 steps, not \(offset)." 29 | ) 30 | } else { 31 | XCTFail("Failed to move and add.") 32 | } 33 | 34 | if case .moveAndAdd(let offset) = diffResults.0[2] { 35 | XCTAssert( 36 | offset == -2, 37 | "w is moved left for 2 steps, not \(offset)." 38 | ) 39 | } else { 40 | XCTFail("Failed to move and add.") 41 | } 42 | 43 | XCTAssert(diffResults.0[4] == .add, "2nd e is added.") 44 | XCTAssert(diffResults.1[5], "The last n was moved in.") 45 | } 46 | 47 | func testStringDiff3() { 48 | let diffResults = "Enchanté".diffWith("Swifter") 49 | 50 | XCTAssert(diffResults.0[4] == .replace, "a is deleted.") 51 | } 52 | 53 | func testStringDiff4() { 54 | let diffResults = "wo".diffWith("ox") 55 | XCTAssert(diffResults.0[0] == .replace, "w is replaced.") 56 | XCTAssert( 57 | diffResults.0[1] == .moveAndAdd(offset: -1), 58 | "o is moved and add a new character." 59 | ) 60 | } 61 | 62 | func testStringDiff5() { 63 | let diffResults = "Objective-C".diffWith("iPhone") 64 | XCTAssert(diffResults.0[0] == .replace, "w is replaced.") 65 | XCTAssert( 66 | diffResults.0[3] == .moveAndAdd(offset: 2), 67 | "e is moved and add a new character." 68 | ) 69 | XCTAssert(diffResults.0[8] == .delete, "2nd e is deleted.") 70 | } 71 | 72 | func testStringDiff6() { 73 | let diffResults = "wow".diffWith("newwow") 74 | XCTAssert(diffResults.0[2] == .moveAndAdd(offset: 1), "2nd. w is moved.") 75 | } 76 | 77 | func testEmojiDiff7() { 78 | let diffResults = "1️⃣2️⃣3️⃣".diffWith("3️⃣1️⃣2️⃣") 79 | XCTAssert(diffResults.0[0] == .moveAndAdd(offset: 1), "1st. 1 is moved.") 80 | } 81 | 82 | func testEmptyDiff8() { 83 | let diffResults0 = "".diffWith("hello") 84 | XCTAssert(diffResults0.0[0] == .add, "Every characters must be added.") 85 | let diffResults1 = "".diffWith("") 86 | XCTAssert(diffResults1.0.count == 0, "Must be empty.") 87 | let diffResults2 = "Hello".diffWith("") 88 | XCTAssert(diffResults2.0[0] == .delete, "Must be empty.") 89 | } 90 | 91 | func testLongDiffPerformance() { 92 | measure { 93 | let lhs = 94 | "Design is not just what it looks like and feels like." 95 | + "Design is how it works." 96 | let rhs = 97 | "Innovation distinguishes between a leader and a follower." 98 | _ = lhs.diffWith(rhs) 99 | } 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /LTMorphingLabelUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /LTMorphingLabelUITests/LTMorphingLabelUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LTMorphingLabelUITests.swift 3 | // LTMorphingLabelUITests 4 | // 5 | // Created by Lex on 25/08/2017. 6 | // Copyright © 2017 lexrus.com. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import MorphingLabel 11 | 12 | class LTMorphingLabelUITests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | 17 | continueAfterFailure = false 18 | XCUIApplication().launch() 19 | } 20 | 21 | override func tearDown() { 22 | super.tearDown() 23 | } 24 | 25 | func testMonkeyTest() { 26 | let app = XCUIApplication() 27 | app.buttons["Evaporate"].tap() 28 | 29 | app.buttons["Fall"].tap() 30 | 31 | let morphinglabelStaticText = XCUIApplication().staticTexts["morphingLabel"] 32 | for _ in 0...20 { 33 | morphinglabelStaticText.tap() 34 | } 35 | } 36 | 37 | func testNilText() { 38 | let app = XCUIApplication() 39 | app.buttons["Clear"].tap() 40 | 41 | let label = app.staticTexts["morphingLabel"] 42 | 43 | guard label.exists else { 44 | XCTFail("must have a morphingLabel with accessibilityIdentifier 'morphingLabel'") 45 | return 46 | } 47 | 48 | app.buttons["Scale"].tap() 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.3 2 | import PackageDescription 3 | 4 | let package = Package( 5 | name: "MorphingLabel", 6 | products: [ 7 | .library(name: "MorphingLabel", targets: ["MorphingLabel"]), 8 | .library(name: "MorphingLabelDynamic", type: .dynamic, targets: ["MorphingLabel"]), 9 | .library(name: "MorphinglabelXCFramework", targets: ["LTMorphingLabel"]) 10 | ], 11 | targets: [ 12 | .target( 13 | name: "MorphingLabel", 14 | exclude: [ 15 | "Info.plist", 16 | "tvOS-Info.plist" 17 | ], 18 | resources: [ 19 | .process("Particles") 20 | ] 21 | ), 22 | .binaryTarget( 23 | name: "LTMorphingLabel", 24 | url: "https://github.com/lexrus/LTMorphingLabel/releases/download/0.9.3/LTMorphingLabel.xcframework.zip", 25 | checksum: "28a0ed8b7df12c763d45b7dde2aa41fd843984b79e6fbd3750f2fc1a6c247a13" 26 | ) 27 | ] 28 | ) 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # LTMorphingLabel 3 | 4 | [![Travis](https://img.shields.io/travis/lexrus/LTMorphingLabel.svg)](https://travis-ci.org/lexrus/LTMorphingLabel) 5 | ![Language](https://img.shields.io/badge/language-Swift%205-orange.svg) 6 | [![CocoaPods](https://img.shields.io/cocoapods/v/LTMorphingLabel.svg?style=flat)](https://github.com/lexrus/LTMorphingLabel) 7 | [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) 8 | [![Accio supported](https://img.shields.io/badge/Accio-supported-0A7CF5.svg?style=flat)](https://github.com/JamitLabs/Accio) 9 | ![License](https://img.shields.io/github/license/lexrus/LTMorphingLabel.svg?style=flat) 10 | 11 | A morphing UILabel subclass written in Swift, originally designed to mimic [Apple's QuickType animation in iOS 8 at WWDC 2014](https://youtu.be/w87fOAG8fjk?t=3451). 12 | 13 | ## Table of Contents 14 | 15 | - [Available Effects](#available-effects) 16 | - [.scale](#scale-default) 17 | - [.evaporate](#evaporate) 18 | - [.fall](#fall) 19 | - [.pixelate](#pixelate) 20 | - [.sparkle](#sparkle) 21 | - [.burn](#burn) 22 | - [Usage](#usage) 23 | - [UIKit](#uikit) 24 | - [SwiftUI](#swiftui) 25 | - [Requirements](#requirements) 26 | - [Installation](#installation) 27 | - [Swift Package Manager](#swift-package-manager) 28 | - [CocoaPods](#cocoapods) 29 | - [Carthage](#carthage) 30 | - [XCFramework](#xcframework) 31 | - [Accio](#accio) 32 | - [Unit Testing](#unit-testing) 33 | - [Apps Using `LTMorphingLabel`](#apps-using-ltmorphinglabel) 34 | - [Third-Party Ports](#third-party-ports) 35 | - [Android](#android) 36 | - [React Native](#react-native) 37 | - [License](#license) 38 | 39 | ## Available Effects 40 | 41 | #### .scale (_default_) 42 | LTMorphingLabel 43 | 44 | #### [.evaporate](https://github.com/lexrus/LTMorphingLabel/blob/master/LTMorphingLabel/LTMorphingLabel%2BEvaporate.swift) 45 | LTMorphingLabel-Evaporate 46 | 47 | #### [.fall](https://github.com/lexrus/LTMorphingLabel/blob/master/LTMorphingLabel/LTMorphingLabel%2BFall.swift) 48 | LTMorphingLabel-Fall 49 | 50 | #### [.pixelate](https://github.com/lexrus/LTMorphingLabel/blob/master/LTMorphingLabel/LTMorphingLabel%2BPixelate.swift) 51 | LTMorphingLabel-Pixelate 52 | 53 | #### [.sparkle](https://github.com/lexrus/LTMorphingLabel/blob/master/LTMorphingLabel/LTMorphingLabel%2BSparkle.swift) 54 | LTMorphingLabel-Sparkle 55 | 56 | > ```.sparkle``` is built on top of `QuartzCore.CAEmitterLayer`. There is also a SpriteKit-powered version [here](https://github.com/lexrus/LTMorphingLabel/blob/spritekit-sparkle/LTMorphingLabel/LTMorphingLabel%2BSparkle.swift). 57 | 58 | #### [.burn](https://github.com/lexrus/LTMorphingLabel/blob/master/LTMorphingLabel/LTMorphingLabel%2BBurn.swift) 59 | LTMorphingLabel-Burn 60 | 61 | #### [.anvil](https://github.com/lexrus/LTMorphingLabel/blob/master/LTMorphingLabel/LTMorphingLabel%2BAnvil.swift) 62 | LTMorphingLabel-Anvil 63 | 64 | ## Usage 65 | 66 | ### UIKit 67 | 68 | Since `LTMorphingLabel` is a drop-in replacement, you can use it like any `UILabel` by setting its text property, yielding the default effect (`.scale`): 69 | 70 | ```swift 71 | var exampleLabel = LTMorphingLabel() 72 | exampleLabel.text = "This is a test" 73 | ``` 74 | ![UIKitExample1](https://github.com/jonathanwuki/LTMorphingLabel/assets/11484046/bba7138e-1ef8-4714-8792-f34349434761) 75 | 76 | Alternatively, it can be used interactively: 77 | ```swift 78 | var exampleLabel = LTMorphingLabel() 79 | exampleLabel.text = "This is a test" 80 | exampleLabel.pause() 81 | 82 | // Call .updateProgress(progress: Float) for interactive animation 83 | // Note: In this case, animation will stop at 45% and will not complete 84 | // unless called later with 100 as the `progress` float value. 85 | exampleLabel.updateProgress(progress: 45.0) 86 | ``` 87 | 88 | The effect can be changed by setting the `morphingEffect` property: 89 | ```swift 90 | var exampleLabel = LTMorphingLabel() 91 | exampleLabel.text = "This is a test" 92 | exampleLabel.morphingEffect = .burn 93 | ``` 94 | ![UIKitExample2](https://github.com/jonathanwuki/LTMorphingLabel/assets/11484046/13ab83a6-7f1f-4aac-b0eb-d6172773a671) 95 | 96 | ### SwiftUI 97 | 98 | To use `LTMorphingLabel` in SwiftUI, simply declare it and set its text, `effect`, `font`, and `textColor` properties: 99 | ```swift 100 | public var body: some View { 101 | VStack { 102 | MorphingText( 103 | "This is a test", 104 | font: UIFont.systemFont(ofSize: 20), 105 | textColor: .black, 106 | textAlignment: .center 107 | ) 108 | .frame(maxWidth: 200, maxHeight: 100) 109 | } 110 | } 111 | ``` 112 | 113 | Similar to its use in UIKit, you can also specify the morphing effect manually (if you do not want to use the default `.scale` effect): 114 | ```swift 115 | public var body: some View { 116 | VStack { 117 | MorphingText( 118 | "This is a test", 119 | effect: .burn, // Specify an alternative morphing effect with this line 120 | font: UIFont.systemFont(ofSize: 20), 121 | textColor: .black, 122 | textAlignment: .center 123 | ) 124 | .frame(maxWidth: 200, maxHeight: 100) 125 | } 126 | } 127 | ``` 128 | ![LTMorphingLabelSwiftUI](https://user-images.githubusercontent.com/219689/81505494-2c528c80-9322-11ea-9bdb-b208dd38a5e6.png) 129 | 130 | ## Requirements 131 | 132 | - Xcode 12+ 133 | - iOS 9.0+ (note that SwiftUI requires iOS 13+) 134 | 135 | ## Installation 136 | 137 | ### [Swift Package Manager](https://swift.org/package-manager/) 138 | 139 | Simply add this library to your package manifest or follow instructions on adding a package dependency [using Xcode here](https://developer.apple.com/documentation/xcode/adding_package_dependencies_to_your_app). 140 | ```swift 141 | .package( 142 | url: "https://github.com/lexrus/LTMorphingLabel.git", 143 | .branch("master") 144 | ) 145 | ``` 146 | 147 | ### [CocoaPods](http://cocoapods.org) 148 | 149 | Add `pod 'LTMorphingLabel'` to your Podfile or follow instructions to add dependencies [here](https://guides.cocoapods.org/using/using-cocoapods.html). 150 | 151 | ### [Carthage](https://github.com/Carthage/Carthage) 152 | 153 | Add `github "lexrus/LTMorphingLabel"` to your Cartfile or follow instructions on adding frameworks [here](https://github.com/Carthage/Carthage#adding-frameworks-to-an-application). 154 | 155 | ### [XCFramework](https://developer.apple.com/videos/play/wwdc2019/416/) 156 | 157 | A pre-compiled `xcframework` file is available on the [Releases](https://github.com/lexrus/LTMorphingLabel/releases) page. 158 | 159 | ### [Accio](https://github.com/JamitLabs/Accio) 160 | 161 | 1. Add this library to your package manifest (see [Swift Package Manager](#swift-package-manager)) 162 | 163 | 2. Update your target dependencies to include `LTMorphingLabel`: 164 | 165 | ```swift 166 | .target( 167 | name: "App", 168 | dependencies: [ 169 | "LTMorphingLabel", 170 | ] 171 | ), 172 | ``` 173 | 174 | 3. Run `accio update`. 175 | 176 | ## Unit Testing 177 | 178 | Clone the repo by running `git clone https://github.com/lexrus/LTMorphingLabel.git`, then open the project with Xcode and press Cmd + U (or, in the menu bar, click Product > Build for > Testing). 179 | 180 | ## Apps Using `LTMorphingLabel` 181 | - [Idea](https://itunes.apple.com/app/id1286758943) by [Igor Matyushkin](https://github.com/igormatyushkin014) 182 | - [Speedo[kilo]meter](https://itunes.apple.com/it/app/speedo-kilo-meter/id1228840413?mt=8) by [Alberto Pasca](http://www.albertopasca.it/whiletrue) 183 | - [Vatomium](http://vatomium.com) by [Erik Telepovský](http://pragmaticmates.com) 184 | - [Atmos](http://www.atmosapp.com) by [@shnhrrsn](https://github.com/shnhrrsn) 185 | - [The Met Challenge](https://itunes.apple.com/us/app/the-met-challenge/id917662781) by [@lazerwalker](https://github.com/lazerwalker) 186 | - [Uther](https://github.com/callmewhy/Uther) by [@callmewhy](https://github.com/callmewhy) 187 | - [Reax](https://itunes.apple.com/us/app/reax-witness-2016-here.-now./id1076183758?ls=1&mt=8) by Reax Inc 188 | - [Puzzpic](https://itunes.apple.com/us/app/puzzpic/id1092871121) by [Moath Othman](http://moathothman.com) 189 | - [Drops](http://languagedrops.com) by [Mark Aron Szulyovszky](https://github.com/itchingpixels) 190 | - [Setgraph Workout Log](https://itunes.apple.com/us/app/setgraph-workout-log/id1209781676?mt=8) by [Arturo Lee](https://github.com/ArturoLee) 191 | - [Nihon](https://itunes.apple.com/app/id1315486029) by [KyXu](https://github.com/OpenMarshall) 192 | - [Lightsync](https://itunes.apple.com/app/id1463390406?mt=8&ct=ghltml) by [Marcel Braun](https://github.com/thatmarcel) 193 | - [Find](https://apps.apple.com/app/find-command-f-for-camera/id1506500202) by [A. Zheng](https://github.com/aheze) 194 | 195 | ## Third-Party Ports 196 | 197 | ### Android 198 | 199 | The Android port of this library is available [here](https://github.com/hanks-zyh/HTextView). 200 | 201 | ### React Native 202 | 203 | The React Native port of this library is available [here](https://github.com/prscX/react-native-morphing-text). 204 | 205 | ## License 206 | 207 | This code is distributed under the terms and conditions of the MIT license. 208 | -------------------------------------------------------------------------------- /Sources/MorphingLabel: -------------------------------------------------------------------------------- 1 | ../LTMorphingLabel -------------------------------------------------------------------------------- /build_xcframework.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | PROJECT_FILE="LTMorphingLabelDemo.xcodeproj" 4 | SCHEME="MorphingLabel" 5 | BUILD_FOLDER="Build/MorphingLabel" 6 | 7 | mkdir -p Build 8 | 9 | # iOS Simulator 10 | xcodebuild archive -project "$PROJECT_FILE" -scheme "$SCHEME" -configuration Release \ 11 | -archivePath "$BUILD_FOLDER/Simulator" \ 12 | -sdk iphonesimulator \ 13 | SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES 14 | 15 | # iOS 16 | xcodebuild archive -project "$PROJECT_FILE" -scheme "$SCHEME" -configuration Release \ 17 | -archivePath "$BUILD_FOLDER/iOS" \ 18 | -sdk iphoneos \ 19 | SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES 20 | 21 | # tvOS 22 | xcodebuild archive -project "$PROJECT_FILE" -scheme "$SCHEME" -configuration Release \ 23 | -archivePath "$BUILD_FOLDER/tvOS" \ 24 | -sdk appletvos \ 25 | SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES 26 | 27 | # tvOS Simulator 28 | xcodebuild archive -project "$PROJECT_FILE" -scheme "$SCHEME" -configuration Release \ 29 | -archivePath "$BUILD_FOLDER/tvOSSimulator" \ 30 | -sdk appletvsimulator \ 31 | SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES 32 | 33 | # XCFramework 34 | xcodebuild -create-xcframework \ 35 | -framework "$BUILD_FOLDER/iOS.xcarchive/Products/Library/Frameworks/MorphingLabel.framework" \ 36 | -framework "$BUILD_FOLDER/tvOS.xcarchive/Products/Library/Frameworks/MorphingLabel.framework" \ 37 | -framework "$BUILD_FOLDER/Simulator.xcarchive/Products/Library/Frameworks/MorphingLabel.framework" \ 38 | -framework "$BUILD_FOLDER/tvOSSimulator.xcarchive/Products/Library/Frameworks/MorphingLabel.framework" \ 39 | -output Build/LTMorphingLabel.xcframework 40 | 41 | cd Build 42 | 43 | # Compress 44 | zip -vry LTMorphingLabel.xcframework.zip LTMorphingLabel.xcframework/ -x "*.DS_Store" 45 | 46 | # Checksum for Package.swift 47 | swift package compute-checksum LTMorphingLabel.xcframework.zip 48 | 49 | # Open in Finder 50 | open ./ 51 | --------------------------------------------------------------------------------