├── .gitignore ├── LICENSE ├── README.md ├── Samples ├── FallingLabel │ ├── .gitignore │ ├── FallingCounterLabel.swift │ ├── FallingLabel.xcodeproj │ │ ├── project.pbxproj │ │ └── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ ├── FallingLabel │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ │ └── Contents.json │ │ ├── Base.lproj │ │ │ ├── LaunchScreen.storyboard │ │ │ └── Main.storyboard │ │ ├── Info.plist │ │ ├── SceneDelegate.swift │ │ └── ViewController.swift │ └── Podfile ├── FlyingText │ ├── .gitignore │ ├── FlyingText.xcodeproj │ │ ├── project.pbxproj │ │ └── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ ├── FlyingText │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ │ └── Contents.json │ │ ├── Base.lproj │ │ │ ├── LaunchScreen.storyboard │ │ │ └── Main.storyboard │ │ ├── Info.plist │ │ ├── SceneDelegate.swift │ │ └── ViewController.swift │ └── Podfile ├── TextDecompose │ ├── .gitignore │ ├── Podfile │ ├── TextDecompose.xcodeproj │ │ ├── project.pbxproj │ │ └── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── TextDecompose │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── Contents.json │ │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ │ ├── Info.plist │ │ ├── SceneDelegate.swift │ │ └── ViewController.swift └── TextToSVG │ ├── .gitignore │ ├── Podfile │ ├── TextToSVG.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── TextToSVG │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Info.plist │ ├── SceneDelegate.swift │ └── ViewController.swift ├── Source └── NSAttributedString.swift ├── TextPaths.podspec └── _Assets ├── textpaths-bounds-01.jpg ├── textpaths-bounds-02.jpg ├── textpaths-bounds-03.jpg ├── textpaths-bounds-04.jpg ├── textpaths-falling-uilabel-animation.gif ├── textpaths-fly-in-text-animation.gif ├── textpaths-nsattributedtext-decompose.jpg ├── textpaths-nsattributedtext-to-svg-01.jpg └── textpaths-nsattributedtext-to-svg-02.jpg /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xcuserstate 23 | 24 | ## Obj-C/Swift specific 25 | *.hmap 26 | *.ipa 27 | *.dSYM.zip 28 | *.dSYM 29 | 30 | ## Playgrounds 31 | timeline.xctimeline 32 | playground.xcworkspace 33 | 34 | # Swift Package Manager 35 | # 36 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 37 | # Packages/ 38 | .build/ 39 | 40 | # CocoaPods 41 | # 42 | # We recommend against adding the Pods directory to your .gitignore. However 43 | # you should judge for yourself, the pros and cons are mentioned at: 44 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 45 | # 46 | # Pods/ 47 | 48 | # Carthage 49 | # 50 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 51 | # Carthage/Checkouts 52 | 53 | Carthage/Build 54 | 55 | # fastlane 56 | # 57 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 58 | # screenshots whenever they are needed. 59 | # For more information about the recommended setup visit: 60 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 61 | 62 | fastlane/report.xml 63 | fastlane/Preview.html 64 | fastlane/screenshots 65 | fastlane/test_output 66 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Matt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TextPaths 2 | 3 | **TextPaths** is an utility for converting `NSAttributedText` to vector representation where each character of an input text is represented as a `CGPAth` glyph. **TextPaths** also returns typographic propeties for lines and entire text flow. For in-depth knowledge on how iOS handles text see [Using Text Kit to Draw and Manage Text](https://developer.apple.com/library/archive/documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/CustomTextProcessing/CustomTextProcessing.html) chapter in documentation. 4 | 5 | Source `NSAttributedText` is converted into a tree like representation with char glyphs on leafs. 6 | 7 | ``` 8 | TextPath 9 | ⎿ TextPathFrame[] 10 | ⎿ TextPathLine[] 11 | ⎿ TextPathGlyph[] 12 | 13 | ⎿ ComposedTextPath 14 | ``` 15 | 16 | `TextPathFrame` - text frame representation 17 | 18 | 19 | 20 | `TextPathLine` - single text line representation 21 | 22 | 23 | 24 | There is also a text bounds rectangle available. Text bounds rectangle is equal or less then line bounds rectangle. On image below this metric is show in purple. 25 | 26 | 27 | 28 | `TextPathGlyph` - single character representation (glyph) 29 | 30 | 31 | 32 | `ComposedTextPath` - composed path of entire input text. This is illustrated on all images above in black. 33 | 34 | All illustrations were created with [`TextDecompose`](https://github.com/malczak/TextPaths/tree/master/Samples/TextDecompose) sample application. 35 | 36 | ## Important notes 37 | 38 | - `UITextView` has extra edge insets (padding) of apporx 8px 39 | - `UIKit` treats text different than `CoreText` when different font size is used to trailing line-break `\n` character. TextPaths instead of following `CoreText` is using the same approach as `UIKit`. See code comments in `TextPathLine` class for `effectiveDescent` property. 40 | 41 | ## Production use 42 | 43 | This repo is used in my apps 44 | 45 | - [Fabristic](https://fabristic.com) 46 | - [Last Day? Life progress and stats](https://apps.apple.com/us/app/last-day-track-events-life/id1193076940?ign-mpt=uo%3D4) 47 | 48 | ## Sample Applications 49 | 50 | ### [`TextDecompose`](https://github.com/malczak/TextPaths/tree/master/Samples/TextDecompose) 51 | 52 | A sample showing how NSAttributedString is decomposed into CGPaths keeping all attributes and typographic properties. 53 | See screenshots below 54 | 55 | ### [`TextToSVG`](https://github.com/malczak/TextPaths/tree/master/Samples/TextToSVG) 56 | 57 | NSAttributedString to SVG conversion. Entire NSAttributedString is converted in to one merged CGPath and then serialized as SVG path. Resulting SVG is presented in WKWebView for comparison. 58 | 59 | ### [`FallingLabel`](https://github.com/malczak/TextPaths/tree/master/Samples/FallingLabel) 60 | 61 | Falling letters counter application shows how to to use TextPaths for text animations. In this example TextPaths is used to animated UILabel value change. 62 | 63 | ### [`FlyingText`](https://github.com/malczak/TextPaths/tree/master/Samples/FlyingText) 64 | 65 | Another animation example showing how to animate multiline NSAttributedText. 66 | 67 | ## Screenshots 68 | 69 | ![asas](https://raw.githubusercontent.com/malczak/TextPaths/master/_Assets/textpaths-nsattributedtext-decompose.jpg | height=480) 70 | 71 |
72 | 73 |

TextDecompose

74 |
75 | 76 |
77 | 78 |

TextToSVG #1

79 |
80 | 81 |
82 | 83 |

TextToSVG #2

84 |
85 | 86 |
87 | 88 |

FallingLabel

89 |
90 | 91 |
92 | 93 |

FlyingText

94 |
95 | -------------------------------------------------------------------------------- /Samples/FallingLabel/.gitignore: -------------------------------------------------------------------------------- 1 | Pods/ 2 | Podfile.lock 3 | *.xcworkspace 4 | .DS_Store 5 | 6 | ### Xcode ### 7 | # Xcode 8 | # 9 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 10 | 11 | ## User settings 12 | xcuserdata/ 13 | 14 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 15 | *.xcscmblueprint 16 | *.xccheckout 17 | 18 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 19 | build/ 20 | DerivedData/ 21 | *.moved-aside 22 | *.pbxuser 23 | !default.pbxuser 24 | *.mode1v3 25 | !default.mode1v3 26 | *.mode2v3 27 | !default.mode2v3 28 | *.perspectivev3 29 | !default.perspectivev3 30 | 31 | ## Xcode Patch 32 | *.xcodeproj/* 33 | !*.xcodeproj/project.pbxproj 34 | !*.xcodeproj/xcshareddata/ 35 | !*.xcworkspace/contents.xcworkspacedata 36 | /*.gcno 37 | 38 | ### Xcode Patch ### 39 | **/xcshareddata/WorkspaceSettings.xcsettings 40 | -------------------------------------------------------------------------------- /Samples/FallingLabel/FallingCounterLabel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FallingCounterLabel.swift 3 | // TextPaths 4 | // 5 | // Copyright © 2017 The Pirate Cat. All rights reserved. 6 | // 7 | 8 | import UIKit 9 | import TextPaths 10 | 11 | public class NumberPaths { 12 | 13 | public var font: UIFont 14 | 15 | public var alignment: NSTextAlignment 16 | 17 | public var textPath: TextPath 18 | 19 | public var unicodeMap: [UnicodeScalar:TextPathGlyph] 20 | 21 | public init?(WithFont font: UIFont, alignment: NSTextAlignment) { 22 | self.font = font 23 | self.alignment = alignment 24 | 25 | let par = NSMutableParagraphStyle() 26 | par.alignment = self.alignment 27 | 28 | let numbersString = NSMutableAttributedString(string: "0123456789", attributes: [ 29 | NSAttributedString.Key.font: self.font 30 | ]) 31 | 32 | let compositionSize = CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude) 33 | guard let textPath = numbersString.getTextPath(InBounds: compositionSize) else { 34 | return nil 35 | } 36 | 37 | var paths = [UnicodeScalar:TextPathGlyph]() 38 | let unicodes = numbersString.string.unicodeScalars 39 | textPath.frames[0].enumerateGlyphs({ _, glyph in 40 | let unicode = unicodes[glyph.index] 41 | paths[unicode] = glyph 42 | }) 43 | 44 | self.textPath = textPath 45 | self.unicodeMap = paths 46 | } 47 | } 48 | 49 | public class FallingCounterLabel { 50 | private struct TextDropInfo { 51 | var charPath: TextPathGlyph 52 | var modified: Bool 53 | init(charPath: TextPathGlyph, modified: Bool) { 54 | self.charPath = charPath 55 | self.modified = modified 56 | } 57 | } 58 | 59 | public weak var label: UILabel? { 60 | didSet { 61 | if let label = label { 62 | label.layer.borderColor = UIColor.black.cgColor 63 | label.layer.borderWidth = 1 64 | } 65 | } 66 | } 67 | 68 | public var numberPaths: NumberPaths? 69 | 70 | var layers = [CAShapeLayer]() 71 | 72 | var fieldLength = 2 73 | 74 | var duration = CGFloat(0.6) 75 | 76 | public var value = -1 { 77 | didSet { 78 | update(Animated: (oldValue != -1) && (oldValue != value)) 79 | } 80 | } 81 | 82 | public init(WithLabel label: UILabel) { 83 | self.label = label 84 | } 85 | 86 | func update(Animated animated: Bool) { 87 | guard let numberPaths = numberPaths else { 88 | return 89 | } 90 | 91 | guard let label = label else { 92 | return 93 | } 94 | let attributes = [ 95 | NSAttributedString.Key.font: numberPaths.font 96 | ] 97 | let formatString = String(format: "%%.0%dd", fieldLength) 98 | let numbersString = NSMutableAttributedString(string: String(format: formatString, value), attributes: attributes) 99 | 100 | if animated { 101 | let lastText = label.attributedText 102 | let transition = CATransition() 103 | transition.duration = 0.3 104 | transition.type = CATransitionType.fade 105 | label.layer.add(transition, forKey: "attributedText") 106 | label.attributedText = numbersString 107 | 108 | drop(FromText:lastText!, toText:numbersString) 109 | } else { 110 | label.attributedText = numbersString 111 | } 112 | } 113 | 114 | func drop(FromText fromText: NSAttributedString, toText: NSAttributedString) { 115 | for layer in layers { 116 | layer.removeFromSuperlayer(); 117 | } 118 | 119 | guard let label = label, let numberPaths = numberPaths else { 120 | return 121 | } 122 | 123 | var paths = [TextDropInfo]() 124 | var pathBounds = CGRect.zero 125 | var fromItr = fromText.string.unicodeScalars.makeIterator() 126 | var toItr = toText.string.unicodeScalars.makeIterator() 127 | while let fromScalar = fromItr.next(), let toScalar = toItr.next() { 128 | guard let charPath = numberPaths.unicodeMap[fromScalar] else { 129 | return 130 | } 131 | 132 | pathBounds.size = CGSize(width: pathBounds.width + charPath.advance.width, 133 | height: max(pathBounds.height, charPath.path.boundingBoxOfPath.height)) 134 | let info = TextDropInfo(charPath: charPath, modified: true) 135 | paths.append(info) 136 | } 137 | 138 | var i = 0, j = 0 139 | 140 | var frameOffset = label.bounds.origin 141 | frameOffset.x += (label.bounds.width - pathBounds.width) * 0.5 142 | frameOffset.y += (label.bounds.height - pathBounds.height) * 0.5 143 | 144 | let delays = [1.0, 0.0, 1.5, 4.5, 0.0, 0,0, 0,0, 0,0] 145 | var delay = 0.4 146 | 147 | func d2r(_ value: CGFloat) -> CGFloat { 148 | return value / 180.0 * CGFloat(Float.pi) 149 | } 150 | 151 | var advance = CGFloat(0.0) 152 | var index = 0 153 | for info in paths { 154 | let charPath = info.charPath 155 | let path = CAShapeLayer() 156 | let bounds = charPath.path.boundingBox 157 | 158 | func CT(_ transforms: CATransform3D...) -> CATransform3D { 159 | var T = CATransform3DMakeTranslation(bounds.width*0.5, bounds.height*0.5, 0.0) 160 | for transform in transforms { 161 | T = CATransform3DConcat(transform, T) 162 | } 163 | T = CATransform3DTranslate(T, -bounds.width*0.5, -bounds.height*0.5, 0.0) 164 | return T 165 | } 166 | 167 | if info.modified { 168 | path.path = charPath.path 169 | path.anchorPoint = CGPoint(x: 0.0, y: 0.0) 170 | path.position = CGPoint(x: frameOffset.x + advance, y: frameOffset.y) 171 | path.bounds = CGRect(origin: .zero, size: bounds.size) 172 | path.fillColor = label.textColor.cgColor 173 | path.transform = CATransform3DIdentity 174 | path.opacity = 1.0 175 | label.layer.addSublayer(path) 176 | 177 | let scaleAnim = CABasicAnimation(keyPath: "transform") 178 | scaleAnim.fromValue = CATransform3DIdentity 179 | scaleAnim.toValue = CT(CATransform3DMakeScale(2.0, 2.0, 1.0)) 180 | 181 | var alfa = 20.0 + (5.0 - CGFloat(arc4random_uniform(100))/10.0) 182 | if arc4random_uniform(100)<50 { 183 | alfa *= -1.0 184 | } 185 | 186 | let shakeAnim = CAKeyframeAnimation(keyPath: "transform") 187 | shakeAnim.values = [ 188 | CT(CATransform3DMakeRotation(d2r(0), 0, 0, 1), CATransform3DMakeScale(1.0, 1.0, 1)), 189 | CT(CATransform3DMakeRotation(d2r(0), 0, 0, 1), CATransform3DMakeScale(1.05, 1.05, 1)), 190 | CT(CATransform3DMakeRotation(d2r(-alfa), 0, 0, 1), CATransform3DMakeScale(0.8, 0.8, 1)), 191 | CT(CATransform3DMakeRotation(d2r(+alfa), 0, 0, 1), CATransform3DMakeScale(0.6, 0.6, 1)), 192 | CT(CATransform3DMakeRotation(d2r(0), 0, 0, 1), CATransform3DMakeScale(0.4, 0.4, 1)) 193 | ] 194 | shakeAnim.keyTimes = [0.0, 0.05, 0.25, 0.5, 0.75] 195 | shakeAnim.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeIn) 196 | 197 | let flyAnim = CABasicAnimation(keyPath: "position.y") 198 | flyAnim.isAdditive = true 199 | flyAnim.fromValue = 0.1 200 | flyAnim.toValue = (2.0 + 2.5 * CGFloat(arc4random_uniform(100))/100.0) * bounds.height 201 | flyAnim.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeIn) 202 | 203 | let opacityAnim = CABasicAnimation(keyPath: "opacity") 204 | opacityAnim.duration = CFTimeInterval(duration) 205 | opacityAnim.beginTime = 0.2 206 | opacityAnim.fromValue = 1.0 207 | opacityAnim.toValue = 0.0 208 | 209 | let groupAnim = CAAnimationGroup() 210 | groupAnim.beginTime = CACurrentMediaTime() 211 | groupAnim.duration = CFTimeInterval(duration) + 0.2 212 | groupAnim.animations = [scaleAnim, shakeAnim, flyAnim, opacityAnim] 213 | groupAnim.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeIn) 214 | groupAnim.fillMode = CAMediaTimingFillMode.forwards 215 | groupAnim.isRemovedOnCompletion = false 216 | 217 | path.removeAllAnimations() 218 | path.add(groupAnim, forKey: "animation") 219 | layers.append(path) 220 | } 221 | 222 | advance += charPath.advance.width 223 | index += 1 224 | } 225 | } 226 | 227 | } 228 | -------------------------------------------------------------------------------- /Samples/FallingLabel/FallingLabel.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 324B5FF0234D2C90009FA70D /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 324B5FEF234D2C90009FA70D /* AppDelegate.swift */; }; 11 | 324B5FF2234D2C90009FA70D /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 324B5FF1234D2C90009FA70D /* SceneDelegate.swift */; }; 12 | 324B5FF4234D2C90009FA70D /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 324B5FF3234D2C90009FA70D /* ViewController.swift */; }; 13 | 324B5FF7234D2C90009FA70D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 324B5FF5234D2C90009FA70D /* Main.storyboard */; }; 14 | 324B5FF9234D2C92009FA70D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 324B5FF8234D2C92009FA70D /* Assets.xcassets */; }; 15 | 324B5FFC234D2C92009FA70D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 324B5FFA234D2C92009FA70D /* LaunchScreen.storyboard */; }; 16 | 324B6004234D2DA9009FA70D /* FallingCounterLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 324B6003234D2DA9009FA70D /* FallingCounterLabel.swift */; }; 17 | 5973E452FE3F4227A2934B69 /* libPods-FallingLabel.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 715C740E4515FE18A3B192EB /* libPods-FallingLabel.a */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXFileReference section */ 21 | 324B5FEC234D2C90009FA70D /* FallingLabel.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FallingLabel.app; sourceTree = BUILT_PRODUCTS_DIR; }; 22 | 324B5FEF234D2C90009FA70D /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 23 | 324B5FF1234D2C90009FA70D /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; 24 | 324B5FF3234D2C90009FA70D /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 25 | 324B5FF6234D2C90009FA70D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 26 | 324B5FF8234D2C92009FA70D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 27 | 324B5FFB234D2C92009FA70D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 28 | 324B5FFD234D2C92009FA70D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 29 | 324B6003234D2DA9009FA70D /* FallingCounterLabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FallingCounterLabel.swift; sourceTree = SOURCE_ROOT; }; 30 | 3B0D6842C4DC8FFFA31FC4E5 /* Pods-FallingLabel.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FallingLabel.debug.xcconfig"; path = "Target Support Files/Pods-FallingLabel/Pods-FallingLabel.debug.xcconfig"; sourceTree = ""; }; 31 | 715C740E4515FE18A3B192EB /* libPods-FallingLabel.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-FallingLabel.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 32 | 76EC65B8DA2539BB1EEB553E /* Pods-FallingLabel.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FallingLabel.release.xcconfig"; path = "Target Support Files/Pods-FallingLabel/Pods-FallingLabel.release.xcconfig"; sourceTree = ""; }; 33 | /* End PBXFileReference section */ 34 | 35 | /* Begin PBXFrameworksBuildPhase section */ 36 | 324B5FE9234D2C90009FA70D /* Frameworks */ = { 37 | isa = PBXFrameworksBuildPhase; 38 | buildActionMask = 2147483647; 39 | files = ( 40 | 5973E452FE3F4227A2934B69 /* libPods-FallingLabel.a in Frameworks */, 41 | ); 42 | runOnlyForDeploymentPostprocessing = 0; 43 | }; 44 | /* End PBXFrameworksBuildPhase section */ 45 | 46 | /* Begin PBXGroup section */ 47 | 324B5FE3234D2C90009FA70D = { 48 | isa = PBXGroup; 49 | children = ( 50 | 324B5FEE234D2C90009FA70D /* FallingLabel */, 51 | 324B5FED234D2C90009FA70D /* Products */, 52 | 7CEA1D5A1EB379761EC9D9CC /* Pods */, 53 | D6C6D99D1AF8A1B990F28D7D /* Frameworks */, 54 | ); 55 | sourceTree = ""; 56 | }; 57 | 324B5FED234D2C90009FA70D /* Products */ = { 58 | isa = PBXGroup; 59 | children = ( 60 | 324B5FEC234D2C90009FA70D /* FallingLabel.app */, 61 | ); 62 | name = Products; 63 | sourceTree = ""; 64 | }; 65 | 324B5FEE234D2C90009FA70D /* FallingLabel */ = { 66 | isa = PBXGroup; 67 | children = ( 68 | 324B6003234D2DA9009FA70D /* FallingCounterLabel.swift */, 69 | 324B5FEF234D2C90009FA70D /* AppDelegate.swift */, 70 | 324B5FF1234D2C90009FA70D /* SceneDelegate.swift */, 71 | 324B5FF3234D2C90009FA70D /* ViewController.swift */, 72 | 324B5FF5234D2C90009FA70D /* Main.storyboard */, 73 | 324B5FF8234D2C92009FA70D /* Assets.xcassets */, 74 | 324B5FFA234D2C92009FA70D /* LaunchScreen.storyboard */, 75 | 324B5FFD234D2C92009FA70D /* Info.plist */, 76 | ); 77 | path = FallingLabel; 78 | sourceTree = ""; 79 | }; 80 | 7CEA1D5A1EB379761EC9D9CC /* Pods */ = { 81 | isa = PBXGroup; 82 | children = ( 83 | 3B0D6842C4DC8FFFA31FC4E5 /* Pods-FallingLabel.debug.xcconfig */, 84 | 76EC65B8DA2539BB1EEB553E /* Pods-FallingLabel.release.xcconfig */, 85 | ); 86 | path = Pods; 87 | sourceTree = ""; 88 | }; 89 | D6C6D99D1AF8A1B990F28D7D /* Frameworks */ = { 90 | isa = PBXGroup; 91 | children = ( 92 | 715C740E4515FE18A3B192EB /* libPods-FallingLabel.a */, 93 | ); 94 | name = Frameworks; 95 | sourceTree = ""; 96 | }; 97 | /* End PBXGroup section */ 98 | 99 | /* Begin PBXNativeTarget section */ 100 | 324B5FEB234D2C90009FA70D /* FallingLabel */ = { 101 | isa = PBXNativeTarget; 102 | buildConfigurationList = 324B6000234D2C92009FA70D /* Build configuration list for PBXNativeTarget "FallingLabel" */; 103 | buildPhases = ( 104 | 21614D6E4B8C752D862AD1AF /* [CP] Check Pods Manifest.lock */, 105 | 324B5FE8234D2C90009FA70D /* Sources */, 106 | 324B5FE9234D2C90009FA70D /* Frameworks */, 107 | 324B5FEA234D2C90009FA70D /* Resources */, 108 | ); 109 | buildRules = ( 110 | ); 111 | dependencies = ( 112 | ); 113 | name = FallingLabel; 114 | productName = FallingLabel; 115 | productReference = 324B5FEC234D2C90009FA70D /* FallingLabel.app */; 116 | productType = "com.apple.product-type.application"; 117 | }; 118 | /* End PBXNativeTarget section */ 119 | 120 | /* Begin PBXProject section */ 121 | 324B5FE4234D2C90009FA70D /* Project object */ = { 122 | isa = PBXProject; 123 | attributes = { 124 | LastSwiftUpdateCheck = 1100; 125 | LastUpgradeCheck = 1100; 126 | TargetAttributes = { 127 | 324B5FEB234D2C90009FA70D = { 128 | CreatedOnToolsVersion = 11.0; 129 | }; 130 | }; 131 | }; 132 | buildConfigurationList = 324B5FE7234D2C90009FA70D /* Build configuration list for PBXProject "FallingLabel" */; 133 | compatibilityVersion = "Xcode 9.3"; 134 | developmentRegion = en; 135 | hasScannedForEncodings = 0; 136 | knownRegions = ( 137 | en, 138 | Base, 139 | ); 140 | mainGroup = 324B5FE3234D2C90009FA70D; 141 | productRefGroup = 324B5FED234D2C90009FA70D /* Products */; 142 | projectDirPath = ""; 143 | projectRoot = ""; 144 | targets = ( 145 | 324B5FEB234D2C90009FA70D /* FallingLabel */, 146 | ); 147 | }; 148 | /* End PBXProject section */ 149 | 150 | /* Begin PBXResourcesBuildPhase section */ 151 | 324B5FEA234D2C90009FA70D /* Resources */ = { 152 | isa = PBXResourcesBuildPhase; 153 | buildActionMask = 2147483647; 154 | files = ( 155 | 324B5FFC234D2C92009FA70D /* LaunchScreen.storyboard in Resources */, 156 | 324B5FF9234D2C92009FA70D /* Assets.xcassets in Resources */, 157 | 324B5FF7234D2C90009FA70D /* Main.storyboard in Resources */, 158 | ); 159 | runOnlyForDeploymentPostprocessing = 0; 160 | }; 161 | /* End PBXResourcesBuildPhase section */ 162 | 163 | /* Begin PBXShellScriptBuildPhase section */ 164 | 21614D6E4B8C752D862AD1AF /* [CP] Check Pods Manifest.lock */ = { 165 | isa = PBXShellScriptBuildPhase; 166 | buildActionMask = 2147483647; 167 | files = ( 168 | ); 169 | inputFileListPaths = ( 170 | ); 171 | inputPaths = ( 172 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 173 | "${PODS_ROOT}/Manifest.lock", 174 | ); 175 | name = "[CP] Check Pods Manifest.lock"; 176 | outputFileListPaths = ( 177 | ); 178 | outputPaths = ( 179 | "$(DERIVED_FILE_DIR)/Pods-FallingLabel-checkManifestLockResult.txt", 180 | ); 181 | runOnlyForDeploymentPostprocessing = 0; 182 | shellPath = /bin/sh; 183 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 184 | showEnvVarsInLog = 0; 185 | }; 186 | /* End PBXShellScriptBuildPhase section */ 187 | 188 | /* Begin PBXSourcesBuildPhase section */ 189 | 324B5FE8234D2C90009FA70D /* Sources */ = { 190 | isa = PBXSourcesBuildPhase; 191 | buildActionMask = 2147483647; 192 | files = ( 193 | 324B5FF4234D2C90009FA70D /* ViewController.swift in Sources */, 194 | 324B6004234D2DA9009FA70D /* FallingCounterLabel.swift in Sources */, 195 | 324B5FF0234D2C90009FA70D /* AppDelegate.swift in Sources */, 196 | 324B5FF2234D2C90009FA70D /* SceneDelegate.swift in Sources */, 197 | ); 198 | runOnlyForDeploymentPostprocessing = 0; 199 | }; 200 | /* End PBXSourcesBuildPhase section */ 201 | 202 | /* Begin PBXVariantGroup section */ 203 | 324B5FF5234D2C90009FA70D /* Main.storyboard */ = { 204 | isa = PBXVariantGroup; 205 | children = ( 206 | 324B5FF6234D2C90009FA70D /* Base */, 207 | ); 208 | name = Main.storyboard; 209 | sourceTree = ""; 210 | }; 211 | 324B5FFA234D2C92009FA70D /* LaunchScreen.storyboard */ = { 212 | isa = PBXVariantGroup; 213 | children = ( 214 | 324B5FFB234D2C92009FA70D /* Base */, 215 | ); 216 | name = LaunchScreen.storyboard; 217 | sourceTree = ""; 218 | }; 219 | /* End PBXVariantGroup section */ 220 | 221 | /* Begin XCBuildConfiguration section */ 222 | 324B5FFE234D2C92009FA70D /* Debug */ = { 223 | isa = XCBuildConfiguration; 224 | buildSettings = { 225 | ALWAYS_SEARCH_USER_PATHS = NO; 226 | CLANG_ANALYZER_NONNULL = YES; 227 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 228 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 229 | CLANG_CXX_LIBRARY = "libc++"; 230 | CLANG_ENABLE_MODULES = YES; 231 | CLANG_ENABLE_OBJC_ARC = YES; 232 | CLANG_ENABLE_OBJC_WEAK = YES; 233 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 234 | CLANG_WARN_BOOL_CONVERSION = YES; 235 | CLANG_WARN_COMMA = YES; 236 | CLANG_WARN_CONSTANT_CONVERSION = YES; 237 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 238 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 239 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 240 | CLANG_WARN_EMPTY_BODY = YES; 241 | CLANG_WARN_ENUM_CONVERSION = YES; 242 | CLANG_WARN_INFINITE_RECURSION = YES; 243 | CLANG_WARN_INT_CONVERSION = YES; 244 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 245 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 246 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 247 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 248 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 249 | CLANG_WARN_STRICT_PROTOTYPES = YES; 250 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 251 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 252 | CLANG_WARN_UNREACHABLE_CODE = YES; 253 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 254 | COPY_PHASE_STRIP = NO; 255 | DEBUG_INFORMATION_FORMAT = dwarf; 256 | ENABLE_STRICT_OBJC_MSGSEND = YES; 257 | ENABLE_TESTABILITY = YES; 258 | GCC_C_LANGUAGE_STANDARD = gnu11; 259 | GCC_DYNAMIC_NO_PIC = NO; 260 | GCC_NO_COMMON_BLOCKS = YES; 261 | GCC_OPTIMIZATION_LEVEL = 0; 262 | GCC_PREPROCESSOR_DEFINITIONS = ( 263 | "DEBUG=1", 264 | "$(inherited)", 265 | ); 266 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 267 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 268 | GCC_WARN_UNDECLARED_SELECTOR = YES; 269 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 270 | GCC_WARN_UNUSED_FUNCTION = YES; 271 | GCC_WARN_UNUSED_VARIABLE = YES; 272 | IPHONEOS_DEPLOYMENT_TARGET = 13.0; 273 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 274 | MTL_FAST_MATH = YES; 275 | ONLY_ACTIVE_ARCH = YES; 276 | SDKROOT = iphoneos; 277 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 278 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 279 | }; 280 | name = Debug; 281 | }; 282 | 324B5FFF234D2C92009FA70D /* Release */ = { 283 | isa = XCBuildConfiguration; 284 | buildSettings = { 285 | ALWAYS_SEARCH_USER_PATHS = NO; 286 | CLANG_ANALYZER_NONNULL = YES; 287 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 288 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 289 | CLANG_CXX_LIBRARY = "libc++"; 290 | CLANG_ENABLE_MODULES = YES; 291 | CLANG_ENABLE_OBJC_ARC = YES; 292 | CLANG_ENABLE_OBJC_WEAK = YES; 293 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 294 | CLANG_WARN_BOOL_CONVERSION = YES; 295 | CLANG_WARN_COMMA = YES; 296 | CLANG_WARN_CONSTANT_CONVERSION = YES; 297 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 298 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 299 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 300 | CLANG_WARN_EMPTY_BODY = YES; 301 | CLANG_WARN_ENUM_CONVERSION = YES; 302 | CLANG_WARN_INFINITE_RECURSION = YES; 303 | CLANG_WARN_INT_CONVERSION = YES; 304 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 305 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 306 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 307 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 308 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 309 | CLANG_WARN_STRICT_PROTOTYPES = YES; 310 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 311 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 312 | CLANG_WARN_UNREACHABLE_CODE = YES; 313 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 314 | COPY_PHASE_STRIP = NO; 315 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 316 | ENABLE_NS_ASSERTIONS = NO; 317 | ENABLE_STRICT_OBJC_MSGSEND = YES; 318 | GCC_C_LANGUAGE_STANDARD = gnu11; 319 | GCC_NO_COMMON_BLOCKS = YES; 320 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 321 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 322 | GCC_WARN_UNDECLARED_SELECTOR = YES; 323 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 324 | GCC_WARN_UNUSED_FUNCTION = YES; 325 | GCC_WARN_UNUSED_VARIABLE = YES; 326 | IPHONEOS_DEPLOYMENT_TARGET = 13.0; 327 | MTL_ENABLE_DEBUG_INFO = NO; 328 | MTL_FAST_MATH = YES; 329 | SDKROOT = iphoneos; 330 | SWIFT_COMPILATION_MODE = wholemodule; 331 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 332 | VALIDATE_PRODUCT = YES; 333 | }; 334 | name = Release; 335 | }; 336 | 324B6001234D2C92009FA70D /* Debug */ = { 337 | isa = XCBuildConfiguration; 338 | baseConfigurationReference = 3B0D6842C4DC8FFFA31FC4E5 /* Pods-FallingLabel.debug.xcconfig */; 339 | buildSettings = { 340 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 341 | CODE_SIGN_STYLE = Automatic; 342 | DEVELOPMENT_TEAM = HT3GA3Z42X; 343 | INFOPLIST_FILE = FallingLabel/Info.plist; 344 | LD_RUNPATH_SEARCH_PATHS = ( 345 | "$(inherited)", 346 | "@executable_path/Frameworks", 347 | ); 348 | PRODUCT_BUNDLE_IDENTIFIER = cat.thepirate.FallingLabel; 349 | PRODUCT_NAME = "$(TARGET_NAME)"; 350 | SWIFT_VERSION = 5.0; 351 | TARGETED_DEVICE_FAMILY = "1,2"; 352 | }; 353 | name = Debug; 354 | }; 355 | 324B6002234D2C92009FA70D /* Release */ = { 356 | isa = XCBuildConfiguration; 357 | baseConfigurationReference = 76EC65B8DA2539BB1EEB553E /* Pods-FallingLabel.release.xcconfig */; 358 | buildSettings = { 359 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 360 | CODE_SIGN_STYLE = Automatic; 361 | DEVELOPMENT_TEAM = HT3GA3Z42X; 362 | INFOPLIST_FILE = FallingLabel/Info.plist; 363 | LD_RUNPATH_SEARCH_PATHS = ( 364 | "$(inherited)", 365 | "@executable_path/Frameworks", 366 | ); 367 | PRODUCT_BUNDLE_IDENTIFIER = cat.thepirate.FallingLabel; 368 | PRODUCT_NAME = "$(TARGET_NAME)"; 369 | SWIFT_VERSION = 5.0; 370 | TARGETED_DEVICE_FAMILY = "1,2"; 371 | }; 372 | name = Release; 373 | }; 374 | /* End XCBuildConfiguration section */ 375 | 376 | /* Begin XCConfigurationList section */ 377 | 324B5FE7234D2C90009FA70D /* Build configuration list for PBXProject "FallingLabel" */ = { 378 | isa = XCConfigurationList; 379 | buildConfigurations = ( 380 | 324B5FFE234D2C92009FA70D /* Debug */, 381 | 324B5FFF234D2C92009FA70D /* Release */, 382 | ); 383 | defaultConfigurationIsVisible = 0; 384 | defaultConfigurationName = Release; 385 | }; 386 | 324B6000234D2C92009FA70D /* Build configuration list for PBXNativeTarget "FallingLabel" */ = { 387 | isa = XCConfigurationList; 388 | buildConfigurations = ( 389 | 324B6001234D2C92009FA70D /* Debug */, 390 | 324B6002234D2C92009FA70D /* Release */, 391 | ); 392 | defaultConfigurationIsVisible = 0; 393 | defaultConfigurationName = Release; 394 | }; 395 | /* End XCConfigurationList section */ 396 | }; 397 | rootObject = 324B5FE4234D2C90009FA70D /* Project object */; 398 | } 399 | -------------------------------------------------------------------------------- /Samples/FallingLabel/FallingLabel.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Samples/FallingLabel/FallingLabel.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Samples/FallingLabel/FallingLabel/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // FallingLabel 4 | // 5 | // Created by Mateusz Malczak on 08/10/2019. 6 | // 7 | 8 | import UIKit 9 | 10 | @UIApplicationMain 11 | class AppDelegate: UIResponder, UIApplicationDelegate { 12 | 13 | 14 | 15 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 16 | // Override point for customization after application launch. 17 | return true 18 | } 19 | 20 | // MARK: UISceneSession Lifecycle 21 | 22 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { 23 | // Called when a new scene session is being created. 24 | // Use this method to select a configuration to create the new scene with. 25 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) 26 | } 27 | 28 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { 29 | // Called when the user discards a scene session. 30 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. 31 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return. 32 | } 33 | 34 | 35 | } 36 | 37 | -------------------------------------------------------------------------------- /Samples/FallingLabel/FallingLabel/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /Samples/FallingLabel/FallingLabel/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Samples/FallingLabel/FallingLabel/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Samples/FallingLabel/FallingLabel/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /Samples/FallingLabel/FallingLabel/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UIApplicationSceneManifest 24 | 25 | UIApplicationSupportsMultipleScenes 26 | 27 | UISceneConfigurations 28 | 29 | UIWindowSceneSessionRoleApplication 30 | 31 | 32 | UISceneConfigurationName 33 | Default Configuration 34 | UISceneDelegateClassName 35 | $(PRODUCT_MODULE_NAME).SceneDelegate 36 | UISceneStoryboardFile 37 | Main 38 | 39 | 40 | 41 | 42 | UILaunchStoryboardName 43 | LaunchScreen 44 | UIMainStoryboardFile 45 | Main 46 | UIRequiredDeviceCapabilities 47 | 48 | armv7 49 | 50 | UISupportedInterfaceOrientations 51 | 52 | UIInterfaceOrientationPortrait 53 | UIInterfaceOrientationLandscapeLeft 54 | UIInterfaceOrientationLandscapeRight 55 | 56 | UISupportedInterfaceOrientations~ipad 57 | 58 | UIInterfaceOrientationPortrait 59 | UIInterfaceOrientationPortraitUpsideDown 60 | UIInterfaceOrientationLandscapeLeft 61 | UIInterfaceOrientationLandscapeRight 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /Samples/FallingLabel/FallingLabel/SceneDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.swift 3 | // FallingLabel 4 | // 5 | // Created by Mateusz Malczak on 08/10/2019. 6 | // 7 | 8 | import UIKit 9 | 10 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { 11 | 12 | var window: UIWindow? 13 | 14 | 15 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 16 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. 17 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. 18 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). 19 | guard let _ = (scene as? UIWindowScene) else { return } 20 | } 21 | 22 | func sceneDidDisconnect(_ scene: UIScene) { 23 | // Called as the scene is being released by the system. 24 | // This occurs shortly after the scene enters the background, or when its session is discarded. 25 | // Release any resources associated with this scene that can be re-created the next time the scene connects. 26 | // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead). 27 | } 28 | 29 | func sceneDidBecomeActive(_ scene: UIScene) { 30 | // Called when the scene has moved from an inactive state to an active state. 31 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. 32 | } 33 | 34 | func sceneWillResignActive(_ scene: UIScene) { 35 | // Called when the scene will move from an active state to an inactive state. 36 | // This may occur due to temporary interruptions (ex. an incoming phone call). 37 | } 38 | 39 | func sceneWillEnterForeground(_ scene: UIScene) { 40 | // Called as the scene transitions from the background to the foreground. 41 | // Use this method to undo the changes made on entering the background. 42 | } 43 | 44 | func sceneDidEnterBackground(_ scene: UIScene) { 45 | // Called as the scene transitions from the foreground to the background. 46 | // Use this method to save data, release shared resources, and store enough scene-specific state information 47 | // to restore the scene back to its current state. 48 | } 49 | 50 | 51 | } 52 | 53 | -------------------------------------------------------------------------------- /Samples/FallingLabel/FallingLabel/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // FallingLabel 4 | // 5 | // Created by Mateusz Malczak on 08/10/2019. 6 | // 7 | 8 | import UIKit 9 | 10 | fileprivate struct Consts { 11 | static let fonts: [UIFont] = [ 12 | UIFont(name: "AmericanTypewriter-Light", size: 44)!, 13 | UIFont(name: "BradleyHandITCTT-Bold", size: 44)!, 14 | UIFont.systemFont(ofSize: 120, weight: .regular), 15 | UIFont.systemFont(ofSize: 60, weight: .ultraLight), 16 | UIFont(name: "Zapfino", size: 33)!, 17 | UIFont.systemFont(ofSize: 40, weight: .bold), 18 | ] 19 | } 20 | 21 | class ViewController: UIViewController { 22 | 23 | @IBOutlet weak var label: UILabel! 24 | 25 | var counterDisplay: FallingCounterLabel? 26 | 27 | var timer: Timer? 28 | 29 | var currentFontIndex: Int = 0 30 | 31 | var index: Int = 0 32 | 33 | 34 | override func viewDidLoad() { 35 | super.viewDidLoad() 36 | 37 | let counter = FallingCounterLabel(WithLabel: label) 38 | counter.duration = 0.8 39 | counter.value = 0 40 | self.counterDisplay = counter 41 | } 42 | 43 | override func viewDidAppear(_ animated: Bool) { 44 | super.viewDidAppear(animated) 45 | setFont() 46 | startTimer() 47 | } 48 | 49 | override func viewDidDisappear(_ animated: Bool) { 50 | stopTimer() 51 | super.viewDidDisappear(animated) 52 | } 53 | 54 | func setFont(){ 55 | guard let counter = counterDisplay else { 56 | return 57 | } 58 | 59 | counter.numberPaths = NumberPaths(WithFont: Consts.fonts[currentFontIndex], 60 | alignment: .center) 61 | 62 | } 63 | 64 | func startTimer() { 65 | stopTimer() 66 | index = 0 67 | let timer = Timer.scheduledTimer(timeInterval: 1.0, 68 | target: self, 69 | selector: #selector(onTimer), 70 | userInfo: nil, 71 | repeats: true) 72 | self.timer = timer 73 | } 74 | 75 | func stopTimer() { 76 | if let timer = timer { 77 | timer.invalidate() 78 | } 79 | timer = nil 80 | } 81 | 82 | @objc func onTimer(_ t: Any) { 83 | guard let counter = counterDisplay else { 84 | return 85 | } 86 | let value = counter.value 87 | counter.value = value + 1 88 | 89 | index += 1; 90 | if (index % 10) == 0 { 91 | currentFontIndex = (currentFontIndex + 1) % Consts.fonts.count 92 | setFont() 93 | } 94 | } 95 | 96 | } 97 | 98 | -------------------------------------------------------------------------------- /Samples/FallingLabel/Podfile: -------------------------------------------------------------------------------- 1 | target 'FallingLabel' 2 | pod 'TextPaths', :path => './../..' 3 | -------------------------------------------------------------------------------- /Samples/FlyingText/.gitignore: -------------------------------------------------------------------------------- 1 | Pods/ 2 | Podfile.lock 3 | *.xcworkspace 4 | .DS_Store 5 | 6 | ### Xcode ### 7 | # Xcode 8 | # 9 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 10 | 11 | ## User settings 12 | xcuserdata/ 13 | 14 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 15 | *.xcscmblueprint 16 | *.xccheckout 17 | 18 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 19 | build/ 20 | DerivedData/ 21 | *.moved-aside 22 | *.pbxuser 23 | !default.pbxuser 24 | *.mode1v3 25 | !default.mode1v3 26 | *.mode2v3 27 | !default.mode2v3 28 | *.perspectivev3 29 | !default.perspectivev3 30 | 31 | ## Xcode Patch 32 | *.xcodeproj/* 33 | !*.xcodeproj/project.pbxproj 34 | !*.xcodeproj/xcshareddata/ 35 | !*.xcworkspace/contents.xcworkspacedata 36 | /*.gcno 37 | 38 | ### Xcode Patch ### 39 | **/xcshareddata/WorkspaceSettings.xcsettings 40 | -------------------------------------------------------------------------------- /Samples/FlyingText/FlyingText.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 324B6012234D34D8009FA70D /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 324B6011234D34D8009FA70D /* AppDelegate.swift */; }; 11 | 324B6014234D34D8009FA70D /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 324B6013234D34D8009FA70D /* SceneDelegate.swift */; }; 12 | 324B6016234D34D8009FA70D /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 324B6015234D34D8009FA70D /* ViewController.swift */; }; 13 | 324B6019234D34D8009FA70D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 324B6017234D34D8009FA70D /* Main.storyboard */; }; 14 | 324B601B234D34DB009FA70D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 324B601A234D34DB009FA70D /* Assets.xcassets */; }; 15 | 324B601E234D34DB009FA70D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 324B601C234D34DB009FA70D /* LaunchScreen.storyboard */; }; 16 | 784AA094C8646162F00FB3FB /* libPods-FlyingText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F5B1456C8EC7BD5839337514 /* libPods-FlyingText.a */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXFileReference section */ 20 | 182A1A11CC5C220406806CAB /* Pods-FlyingText.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FlyingText.debug.xcconfig"; path = "Target Support Files/Pods-FlyingText/Pods-FlyingText.debug.xcconfig"; sourceTree = ""; }; 21 | 324B600E234D34D8009FA70D /* FlyingText.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FlyingText.app; sourceTree = BUILT_PRODUCTS_DIR; }; 22 | 324B6011234D34D8009FA70D /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 23 | 324B6013234D34D8009FA70D /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; 24 | 324B6015234D34D8009FA70D /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 25 | 324B6018234D34D8009FA70D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 26 | 324B601A234D34DB009FA70D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 27 | 324B601D234D34DB009FA70D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 28 | 324B601F234D34DB009FA70D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 29 | 4B7D5DDDF312538D0565C98B /* Pods-FlyingText.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FlyingText.release.xcconfig"; path = "Target Support Files/Pods-FlyingText/Pods-FlyingText.release.xcconfig"; sourceTree = ""; }; 30 | F5B1456C8EC7BD5839337514 /* libPods-FlyingText.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-FlyingText.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 31 | /* End PBXFileReference section */ 32 | 33 | /* Begin PBXFrameworksBuildPhase section */ 34 | 324B600B234D34D8009FA70D /* Frameworks */ = { 35 | isa = PBXFrameworksBuildPhase; 36 | buildActionMask = 2147483647; 37 | files = ( 38 | 784AA094C8646162F00FB3FB /* libPods-FlyingText.a in Frameworks */, 39 | ); 40 | runOnlyForDeploymentPostprocessing = 0; 41 | }; 42 | /* End PBXFrameworksBuildPhase section */ 43 | 44 | /* Begin PBXGroup section */ 45 | 036FD4F9993CFE24B80B2227 /* Frameworks */ = { 46 | isa = PBXGroup; 47 | children = ( 48 | F5B1456C8EC7BD5839337514 /* libPods-FlyingText.a */, 49 | ); 50 | name = Frameworks; 51 | sourceTree = ""; 52 | }; 53 | 268C2AF0A4AF45506FF400BD /* Pods */ = { 54 | isa = PBXGroup; 55 | children = ( 56 | 182A1A11CC5C220406806CAB /* Pods-FlyingText.debug.xcconfig */, 57 | 4B7D5DDDF312538D0565C98B /* Pods-FlyingText.release.xcconfig */, 58 | ); 59 | name = Pods; 60 | path = Pods; 61 | sourceTree = ""; 62 | }; 63 | 324B6005234D34D8009FA70D = { 64 | isa = PBXGroup; 65 | children = ( 66 | 324B6010234D34D8009FA70D /* FlyingText */, 67 | 324B600F234D34D8009FA70D /* Products */, 68 | 268C2AF0A4AF45506FF400BD /* Pods */, 69 | 036FD4F9993CFE24B80B2227 /* Frameworks */, 70 | ); 71 | sourceTree = ""; 72 | }; 73 | 324B600F234D34D8009FA70D /* Products */ = { 74 | isa = PBXGroup; 75 | children = ( 76 | 324B600E234D34D8009FA70D /* FlyingText.app */, 77 | ); 78 | name = Products; 79 | sourceTree = ""; 80 | }; 81 | 324B6010234D34D8009FA70D /* FlyingText */ = { 82 | isa = PBXGroup; 83 | children = ( 84 | 324B6011234D34D8009FA70D /* AppDelegate.swift */, 85 | 324B6013234D34D8009FA70D /* SceneDelegate.swift */, 86 | 324B6015234D34D8009FA70D /* ViewController.swift */, 87 | 324B6017234D34D8009FA70D /* Main.storyboard */, 88 | 324B601A234D34DB009FA70D /* Assets.xcassets */, 89 | 324B601C234D34DB009FA70D /* LaunchScreen.storyboard */, 90 | 324B601F234D34DB009FA70D /* Info.plist */, 91 | ); 92 | path = FlyingText; 93 | sourceTree = ""; 94 | }; 95 | /* End PBXGroup section */ 96 | 97 | /* Begin PBXNativeTarget section */ 98 | 324B600D234D34D8009FA70D /* FlyingText */ = { 99 | isa = PBXNativeTarget; 100 | buildConfigurationList = 324B6022234D34DB009FA70D /* Build configuration list for PBXNativeTarget "FlyingText" */; 101 | buildPhases = ( 102 | 67DC25BBB36DFEB45E706902 /* [CP] Check Pods Manifest.lock */, 103 | 324B600A234D34D8009FA70D /* Sources */, 104 | 324B600B234D34D8009FA70D /* Frameworks */, 105 | 324B600C234D34D8009FA70D /* Resources */, 106 | ); 107 | buildRules = ( 108 | ); 109 | dependencies = ( 110 | ); 111 | name = FlyingText; 112 | productName = FlyingText; 113 | productReference = 324B600E234D34D8009FA70D /* FlyingText.app */; 114 | productType = "com.apple.product-type.application"; 115 | }; 116 | /* End PBXNativeTarget section */ 117 | 118 | /* Begin PBXProject section */ 119 | 324B6006234D34D8009FA70D /* Project object */ = { 120 | isa = PBXProject; 121 | attributes = { 122 | LastSwiftUpdateCheck = 1100; 123 | LastUpgradeCheck = 1100; 124 | TargetAttributes = { 125 | 324B600D234D34D8009FA70D = { 126 | CreatedOnToolsVersion = 11.0; 127 | }; 128 | }; 129 | }; 130 | buildConfigurationList = 324B6009234D34D8009FA70D /* Build configuration list for PBXProject "FlyingText" */; 131 | compatibilityVersion = "Xcode 9.3"; 132 | developmentRegion = en; 133 | hasScannedForEncodings = 0; 134 | knownRegions = ( 135 | en, 136 | Base, 137 | ); 138 | mainGroup = 324B6005234D34D8009FA70D; 139 | productRefGroup = 324B600F234D34D8009FA70D /* Products */; 140 | projectDirPath = ""; 141 | projectRoot = ""; 142 | targets = ( 143 | 324B600D234D34D8009FA70D /* FlyingText */, 144 | ); 145 | }; 146 | /* End PBXProject section */ 147 | 148 | /* Begin PBXResourcesBuildPhase section */ 149 | 324B600C234D34D8009FA70D /* Resources */ = { 150 | isa = PBXResourcesBuildPhase; 151 | buildActionMask = 2147483647; 152 | files = ( 153 | 324B601E234D34DB009FA70D /* LaunchScreen.storyboard in Resources */, 154 | 324B601B234D34DB009FA70D /* Assets.xcassets in Resources */, 155 | 324B6019234D34D8009FA70D /* Main.storyboard in Resources */, 156 | ); 157 | runOnlyForDeploymentPostprocessing = 0; 158 | }; 159 | /* End PBXResourcesBuildPhase section */ 160 | 161 | /* Begin PBXShellScriptBuildPhase section */ 162 | 67DC25BBB36DFEB45E706902 /* [CP] Check Pods Manifest.lock */ = { 163 | isa = PBXShellScriptBuildPhase; 164 | buildActionMask = 2147483647; 165 | files = ( 166 | ); 167 | inputFileListPaths = ( 168 | ); 169 | inputPaths = ( 170 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 171 | "${PODS_ROOT}/Manifest.lock", 172 | ); 173 | name = "[CP] Check Pods Manifest.lock"; 174 | outputFileListPaths = ( 175 | ); 176 | outputPaths = ( 177 | "$(DERIVED_FILE_DIR)/Pods-FlyingText-checkManifestLockResult.txt", 178 | ); 179 | runOnlyForDeploymentPostprocessing = 0; 180 | shellPath = /bin/sh; 181 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 182 | showEnvVarsInLog = 0; 183 | }; 184 | /* End PBXShellScriptBuildPhase section */ 185 | 186 | /* Begin PBXSourcesBuildPhase section */ 187 | 324B600A234D34D8009FA70D /* Sources */ = { 188 | isa = PBXSourcesBuildPhase; 189 | buildActionMask = 2147483647; 190 | files = ( 191 | 324B6016234D34D8009FA70D /* ViewController.swift in Sources */, 192 | 324B6012234D34D8009FA70D /* AppDelegate.swift in Sources */, 193 | 324B6014234D34D8009FA70D /* SceneDelegate.swift in Sources */, 194 | ); 195 | runOnlyForDeploymentPostprocessing = 0; 196 | }; 197 | /* End PBXSourcesBuildPhase section */ 198 | 199 | /* Begin PBXVariantGroup section */ 200 | 324B6017234D34D8009FA70D /* Main.storyboard */ = { 201 | isa = PBXVariantGroup; 202 | children = ( 203 | 324B6018234D34D8009FA70D /* Base */, 204 | ); 205 | name = Main.storyboard; 206 | sourceTree = ""; 207 | }; 208 | 324B601C234D34DB009FA70D /* LaunchScreen.storyboard */ = { 209 | isa = PBXVariantGroup; 210 | children = ( 211 | 324B601D234D34DB009FA70D /* Base */, 212 | ); 213 | name = LaunchScreen.storyboard; 214 | sourceTree = ""; 215 | }; 216 | /* End PBXVariantGroup section */ 217 | 218 | /* Begin XCBuildConfiguration section */ 219 | 324B6020234D34DB009FA70D /* Debug */ = { 220 | isa = XCBuildConfiguration; 221 | buildSettings = { 222 | ALWAYS_SEARCH_USER_PATHS = NO; 223 | CLANG_ANALYZER_NONNULL = YES; 224 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 225 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 226 | CLANG_CXX_LIBRARY = "libc++"; 227 | CLANG_ENABLE_MODULES = YES; 228 | CLANG_ENABLE_OBJC_ARC = YES; 229 | CLANG_ENABLE_OBJC_WEAK = YES; 230 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 231 | CLANG_WARN_BOOL_CONVERSION = YES; 232 | CLANG_WARN_COMMA = YES; 233 | CLANG_WARN_CONSTANT_CONVERSION = YES; 234 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 235 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 236 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 237 | CLANG_WARN_EMPTY_BODY = YES; 238 | CLANG_WARN_ENUM_CONVERSION = YES; 239 | CLANG_WARN_INFINITE_RECURSION = YES; 240 | CLANG_WARN_INT_CONVERSION = YES; 241 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 242 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 243 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 244 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 245 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 246 | CLANG_WARN_STRICT_PROTOTYPES = YES; 247 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 248 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 249 | CLANG_WARN_UNREACHABLE_CODE = YES; 250 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 251 | COPY_PHASE_STRIP = NO; 252 | DEBUG_INFORMATION_FORMAT = dwarf; 253 | ENABLE_STRICT_OBJC_MSGSEND = YES; 254 | ENABLE_TESTABILITY = YES; 255 | GCC_C_LANGUAGE_STANDARD = gnu11; 256 | GCC_DYNAMIC_NO_PIC = NO; 257 | GCC_NO_COMMON_BLOCKS = YES; 258 | GCC_OPTIMIZATION_LEVEL = 0; 259 | GCC_PREPROCESSOR_DEFINITIONS = ( 260 | "DEBUG=1", 261 | "$(inherited)", 262 | ); 263 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 264 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 265 | GCC_WARN_UNDECLARED_SELECTOR = YES; 266 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 267 | GCC_WARN_UNUSED_FUNCTION = YES; 268 | GCC_WARN_UNUSED_VARIABLE = YES; 269 | IPHONEOS_DEPLOYMENT_TARGET = 13.0; 270 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 271 | MTL_FAST_MATH = YES; 272 | ONLY_ACTIVE_ARCH = YES; 273 | SDKROOT = iphoneos; 274 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 275 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 276 | }; 277 | name = Debug; 278 | }; 279 | 324B6021234D34DB009FA70D /* Release */ = { 280 | isa = XCBuildConfiguration; 281 | buildSettings = { 282 | ALWAYS_SEARCH_USER_PATHS = NO; 283 | CLANG_ANALYZER_NONNULL = YES; 284 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 285 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 286 | CLANG_CXX_LIBRARY = "libc++"; 287 | CLANG_ENABLE_MODULES = YES; 288 | CLANG_ENABLE_OBJC_ARC = YES; 289 | CLANG_ENABLE_OBJC_WEAK = YES; 290 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 291 | CLANG_WARN_BOOL_CONVERSION = YES; 292 | CLANG_WARN_COMMA = YES; 293 | CLANG_WARN_CONSTANT_CONVERSION = YES; 294 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 295 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 296 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 297 | CLANG_WARN_EMPTY_BODY = YES; 298 | CLANG_WARN_ENUM_CONVERSION = YES; 299 | CLANG_WARN_INFINITE_RECURSION = YES; 300 | CLANG_WARN_INT_CONVERSION = YES; 301 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 302 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 303 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 304 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 305 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 306 | CLANG_WARN_STRICT_PROTOTYPES = YES; 307 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 308 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 309 | CLANG_WARN_UNREACHABLE_CODE = YES; 310 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 311 | COPY_PHASE_STRIP = NO; 312 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 313 | ENABLE_NS_ASSERTIONS = NO; 314 | ENABLE_STRICT_OBJC_MSGSEND = YES; 315 | GCC_C_LANGUAGE_STANDARD = gnu11; 316 | GCC_NO_COMMON_BLOCKS = YES; 317 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 318 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 319 | GCC_WARN_UNDECLARED_SELECTOR = YES; 320 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 321 | GCC_WARN_UNUSED_FUNCTION = YES; 322 | GCC_WARN_UNUSED_VARIABLE = YES; 323 | IPHONEOS_DEPLOYMENT_TARGET = 13.0; 324 | MTL_ENABLE_DEBUG_INFO = NO; 325 | MTL_FAST_MATH = YES; 326 | SDKROOT = iphoneos; 327 | SWIFT_COMPILATION_MODE = wholemodule; 328 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 329 | VALIDATE_PRODUCT = YES; 330 | }; 331 | name = Release; 332 | }; 333 | 324B6023234D34DB009FA70D /* Debug */ = { 334 | isa = XCBuildConfiguration; 335 | baseConfigurationReference = 182A1A11CC5C220406806CAB /* Pods-FlyingText.debug.xcconfig */; 336 | buildSettings = { 337 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 338 | CODE_SIGN_STYLE = Automatic; 339 | DEVELOPMENT_TEAM = HT3GA3Z42X; 340 | INFOPLIST_FILE = FlyingText/Info.plist; 341 | LD_RUNPATH_SEARCH_PATHS = ( 342 | "$(inherited)", 343 | "@executable_path/Frameworks", 344 | ); 345 | PRODUCT_BUNDLE_IDENTIFIER = cat.thepirate.FlyingText; 346 | PRODUCT_NAME = "$(TARGET_NAME)"; 347 | SWIFT_VERSION = 5.0; 348 | TARGETED_DEVICE_FAMILY = "1,2"; 349 | }; 350 | name = Debug; 351 | }; 352 | 324B6024234D34DB009FA70D /* Release */ = { 353 | isa = XCBuildConfiguration; 354 | baseConfigurationReference = 4B7D5DDDF312538D0565C98B /* Pods-FlyingText.release.xcconfig */; 355 | buildSettings = { 356 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 357 | CODE_SIGN_STYLE = Automatic; 358 | DEVELOPMENT_TEAM = HT3GA3Z42X; 359 | INFOPLIST_FILE = FlyingText/Info.plist; 360 | LD_RUNPATH_SEARCH_PATHS = ( 361 | "$(inherited)", 362 | "@executable_path/Frameworks", 363 | ); 364 | PRODUCT_BUNDLE_IDENTIFIER = cat.thepirate.FlyingText; 365 | PRODUCT_NAME = "$(TARGET_NAME)"; 366 | SWIFT_VERSION = 5.0; 367 | TARGETED_DEVICE_FAMILY = "1,2"; 368 | }; 369 | name = Release; 370 | }; 371 | /* End XCBuildConfiguration section */ 372 | 373 | /* Begin XCConfigurationList section */ 374 | 324B6009234D34D8009FA70D /* Build configuration list for PBXProject "FlyingText" */ = { 375 | isa = XCConfigurationList; 376 | buildConfigurations = ( 377 | 324B6020234D34DB009FA70D /* Debug */, 378 | 324B6021234D34DB009FA70D /* Release */, 379 | ); 380 | defaultConfigurationIsVisible = 0; 381 | defaultConfigurationName = Release; 382 | }; 383 | 324B6022234D34DB009FA70D /* Build configuration list for PBXNativeTarget "FlyingText" */ = { 384 | isa = XCConfigurationList; 385 | buildConfigurations = ( 386 | 324B6023234D34DB009FA70D /* Debug */, 387 | 324B6024234D34DB009FA70D /* Release */, 388 | ); 389 | defaultConfigurationIsVisible = 0; 390 | defaultConfigurationName = Release; 391 | }; 392 | /* End XCConfigurationList section */ 393 | }; 394 | rootObject = 324B6006234D34D8009FA70D /* Project object */; 395 | } 396 | -------------------------------------------------------------------------------- /Samples/FlyingText/FlyingText.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Samples/FlyingText/FlyingText.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Samples/FlyingText/FlyingText/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // FlyingText 4 | // 5 | // Created by Mateusz Malczak on 08/10/2019. 6 | // 7 | 8 | import UIKit 9 | 10 | @UIApplicationMain 11 | class AppDelegate: UIResponder, UIApplicationDelegate { 12 | 13 | 14 | 15 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 16 | // Override point for customization after application launch. 17 | return true 18 | } 19 | 20 | // MARK: UISceneSession Lifecycle 21 | 22 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { 23 | // Called when a new scene session is being created. 24 | // Use this method to select a configuration to create the new scene with. 25 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) 26 | } 27 | 28 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { 29 | // Called when the user discards a scene session. 30 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. 31 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return. 32 | } 33 | 34 | 35 | } 36 | 37 | -------------------------------------------------------------------------------- /Samples/FlyingText/FlyingText/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /Samples/FlyingText/FlyingText/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Samples/FlyingText/FlyingText/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Samples/FlyingText/FlyingText/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /Samples/FlyingText/FlyingText/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UIApplicationSceneManifest 24 | 25 | UIApplicationSupportsMultipleScenes 26 | 27 | UISceneConfigurations 28 | 29 | UIWindowSceneSessionRoleApplication 30 | 31 | 32 | UISceneConfigurationName 33 | Default Configuration 34 | UISceneDelegateClassName 35 | $(PRODUCT_MODULE_NAME).SceneDelegate 36 | UISceneStoryboardFile 37 | Main 38 | 39 | 40 | 41 | 42 | UILaunchStoryboardName 43 | LaunchScreen 44 | UIMainStoryboardFile 45 | Main 46 | UIRequiredDeviceCapabilities 47 | 48 | armv7 49 | 50 | UISupportedInterfaceOrientations 51 | 52 | UIInterfaceOrientationPortrait 53 | UIInterfaceOrientationLandscapeLeft 54 | UIInterfaceOrientationLandscapeRight 55 | 56 | UISupportedInterfaceOrientations~ipad 57 | 58 | UIInterfaceOrientationPortrait 59 | UIInterfaceOrientationPortraitUpsideDown 60 | UIInterfaceOrientationLandscapeLeft 61 | UIInterfaceOrientationLandscapeRight 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /Samples/FlyingText/FlyingText/SceneDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.swift 3 | // FlyingText 4 | // 5 | // Created by Mateusz Malczak on 08/10/2019. 6 | // 7 | 8 | import UIKit 9 | 10 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { 11 | 12 | var window: UIWindow? 13 | 14 | 15 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 16 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. 17 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. 18 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). 19 | guard let _ = (scene as? UIWindowScene) else { return } 20 | } 21 | 22 | func sceneDidDisconnect(_ scene: UIScene) { 23 | // Called as the scene is being released by the system. 24 | // This occurs shortly after the scene enters the background, or when its session is discarded. 25 | // Release any resources associated with this scene that can be re-created the next time the scene connects. 26 | // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead). 27 | } 28 | 29 | func sceneDidBecomeActive(_ scene: UIScene) { 30 | // Called when the scene has moved from an inactive state to an active state. 31 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. 32 | } 33 | 34 | func sceneWillResignActive(_ scene: UIScene) { 35 | // Called when the scene will move from an active state to an inactive state. 36 | // This may occur due to temporary interruptions (ex. an incoming phone call). 37 | } 38 | 39 | func sceneWillEnterForeground(_ scene: UIScene) { 40 | // Called as the scene transitions from the background to the foreground. 41 | // Use this method to undo the changes made on entering the background. 42 | } 43 | 44 | func sceneDidEnterBackground(_ scene: UIScene) { 45 | // Called as the scene transitions from the foreground to the background. 46 | // Use this method to save data, release shared resources, and store enough scene-specific state information 47 | // to restore the scene back to its current state. 48 | } 49 | 50 | 51 | } 52 | 53 | -------------------------------------------------------------------------------- /Samples/FlyingText/FlyingText/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // FlyingText 4 | // 5 | // Created by Mateusz Malczak on 08/10/2019. 6 | // 7 | 8 | import UIKit 9 | import TextPaths 10 | 11 | class ViewController: UIViewController { 12 | 13 | @IBOutlet weak var label: UILabel! 14 | 15 | var textPath: TextPath? 16 | 17 | override func viewDidLoad() { 18 | super.viewDidLoad() 19 | 20 | let attributedString = createSlogoText() 21 | let bounds = CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude) 22 | textPath = attributedString.getTextPath(InBounds: bounds, 23 | withAttributes: true, 24 | withPath: false) 25 | } 26 | 27 | override func viewDidAppear(_ animated: Bool) { 28 | super.viewDidAppear(animated) 29 | slogoFlyin() 30 | } 31 | 32 | func createSlogoText() -> NSAttributedString { 33 | let slogo = "what if\ntoday\nwas your\nlast day?" 34 | let slogoLines = slogo.uppercased().components(separatedBy: .newlines) 35 | 36 | let lineColors = [ 37 | UIColor.blue 38 | ] 39 | let lineFonts = [ 40 | UIFont.systemFont(ofSize: 22, weight: .light), 41 | UIFont.systemFont(ofSize: 42, weight: .ultraLight) 42 | ] 43 | 44 | let attrStr = NSMutableAttributedString() 45 | let para = NSMutableParagraphStyle() 46 | para.alignment = .center 47 | for (index, lineText) in slogoLines.enumerated() { 48 | let color = lineColors[index % lineColors.count] 49 | let font = lineFonts[index % lineFonts.count] 50 | let lineAttrs = [ 51 | NSAttributedString.Key.foregroundColor: color, 52 | NSAttributedString.Key.font: font, 53 | NSAttributedString.Key.paragraphStyle: para 54 | ] 55 | let text = "\n\(lineText)" 56 | let attrLineStr = NSAttributedString(string: text, attributes: lineAttrs) 57 | attrStr.append(attrLineStr) 58 | } 59 | 60 | attrStr.append(NSAttributedString(string: "\n\n\n\nToday will never happen again,\nmake the most of it!", attributes: [ 61 | NSAttributedString.Key.foregroundColor: lineColors[0], 62 | NSAttributedString.Key.font: UIFont.systemFont(ofSize: 16, weight: .ultraLight), 63 | NSAttributedString.Key.paragraphStyle: para 64 | ])) 65 | 66 | return NSAttributedString(attributedString: attrStr) 67 | } 68 | 69 | func slogoFlyin() { 70 | guard let textPath = textPath else { 71 | return 72 | } 73 | 74 | // clean-up 75 | view 76 | .layer 77 | .sublayers? 78 | .compactMap({ $0 as? CAShapeLayer}) 79 | .reversed() 80 | .forEach { $0.removeFromSuperlayer() } 81 | 82 | let frameOffset = CGPoint(x: (view.frame.width - textPath.composedBounds.width) * 0.5, 83 | y: (view.frame.height - textPath.composedBounds.height) * 0.5) 84 | 85 | let duration = 0.2 86 | var i = 0, j = 0 87 | let delays = [0.5, 0.0, 1.0, 1.5] 88 | var delay = 0.2 89 | var time = delay 90 | 91 | var lastAnimation: CAAnimation? = nil 92 | 93 | for line in textPath.frames[0].lines { 94 | line.enumerateGlyphs { [unowned self] _, charPath in 95 | let path = CAShapeLayer() 96 | let bounds = charPath.path.boundingBox 97 | 98 | var T = CATransform3DMakeTranslation(bounds.width*0.5, bounds.height*0.5, 0.0) 99 | T = CATransform3DScale(T, 4.0, 4.0, 1.0) 100 | T = CATransform3DTranslate(T, -bounds.width*0.5, -bounds.height*0.5, 0.0) 101 | 102 | let color = (charPath.attributes?[NSAttributedString.Key.foregroundColor] as? UIColor) ?? UIColor.black 103 | path.path = charPath.path 104 | path.anchorPoint = CGPoint(x: 0.0, y: 0.0) 105 | path.position = CGPoint(x: frameOffset.x + charPath.position.x, y: frameOffset.y + charPath.position.y) 106 | path.bounds = CGRect(origin: .zero, size: bounds.size) 107 | path.fillColor = color.cgColor 108 | path.transform = T 109 | path.opacity = 0.0 110 | self.view.layer.addSublayer(path) 111 | 112 | let scaleAnim = CABasicAnimation(keyPath: "transform") 113 | scaleAnim.fromValue = T 114 | scaleAnim.toValue = CATransform3DIdentity 115 | let opacityAnim = CABasicAnimation(keyPath: "opacity") 116 | opacityAnim.fromValue = 0.0 117 | opacityAnim.toValue = 1.0 118 | let groupAnim = CAAnimationGroup() 119 | groupAnim.beginTime = CACurrentMediaTime() + (duration * 0.3) * Double(j) + delay 120 | groupAnim.duration = duration 121 | groupAnim.animations = [scaleAnim, opacityAnim] 122 | groupAnim.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut) 123 | groupAnim.fillMode = CAMediaTimingFillMode.forwards 124 | groupAnim.isRemovedOnCompletion = false 125 | 126 | path.removeAllAnimations() 127 | path.add(groupAnim, forKey: "animation") 128 | self.view.layer.addSublayer(path) 129 | 130 | lastAnimation = opacityAnim 131 | 132 | j += 1 133 | time += duration * 0.62 134 | } 135 | delay += (i < delays.count) ? delays[i] : 0 136 | i += 1 137 | } 138 | 139 | if let anim = lastAnimation { 140 | anim.delegate = self 141 | } 142 | 143 | DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + DispatchTimeInterval.seconds(Int(round(time))), execute: { [unowned self] in 144 | // self.dismiss(animated: true, completion: nil) 145 | self.slogoFlyin() 146 | }) 147 | } 148 | 149 | } 150 | 151 | extension ViewController: CAAnimationDelegate { 152 | func animationDidStop(_ anim: CAAnimation, finished flag: Bool) { 153 | anim.delegate = nil 154 | DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + DispatchTimeInterval.seconds(1), execute: { [unowned self] in 155 | // replay 156 | self.slogoFlyin() 157 | }) 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /Samples/FlyingText/Podfile: -------------------------------------------------------------------------------- 1 | target 'FlyingText' 2 | pod 'TextPaths', :path => './../..' 3 | -------------------------------------------------------------------------------- /Samples/TextDecompose/.gitignore: -------------------------------------------------------------------------------- 1 | Pods/ 2 | Podfile.lock 3 | *.xcworkspace 4 | .DS_Store 5 | 6 | ### Xcode ### 7 | # Xcode 8 | # 9 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 10 | 11 | ## User settings 12 | xcuserdata/ 13 | 14 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 15 | *.xcscmblueprint 16 | *.xccheckout 17 | 18 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 19 | build/ 20 | DerivedData/ 21 | *.moved-aside 22 | *.pbxuser 23 | !default.pbxuser 24 | *.mode1v3 25 | !default.mode1v3 26 | *.mode2v3 27 | !default.mode2v3 28 | *.perspectivev3 29 | !default.perspectivev3 30 | 31 | ## Xcode Patch 32 | *.xcodeproj/* 33 | !*.xcodeproj/project.pbxproj 34 | !*.xcodeproj/xcshareddata/ 35 | !*.xcworkspace/contents.xcworkspacedata 36 | /*.gcno 37 | 38 | ### Xcode Patch ### 39 | **/xcshareddata/WorkspaceSettings.xcsettings 40 | -------------------------------------------------------------------------------- /Samples/TextDecompose/Podfile: -------------------------------------------------------------------------------- 1 | target 'TextDecompose' 2 | pod 'TextPaths', :path => './../..' 3 | -------------------------------------------------------------------------------- /Samples/TextDecompose/TextDecompose.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 15B95874AD6FD3794E204186 /* libPods-TextDecompose.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E666E8D120942706C134605B /* libPods-TextDecompose.a */; }; 11 | 32039162234E4D7800449B7E /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32039161234E4D7800449B7E /* AppDelegate.swift */; }; 12 | 32039164234E4D7800449B7E /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32039163234E4D7800449B7E /* SceneDelegate.swift */; }; 13 | 32039166234E4D7800449B7E /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32039165234E4D7800449B7E /* ViewController.swift */; }; 14 | 32039169234E4D7800449B7E /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 32039167234E4D7800449B7E /* Main.storyboard */; }; 15 | 3203916B234E4D7A00449B7E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3203916A234E4D7A00449B7E /* Assets.xcassets */; }; 16 | 3203916E234E4D7A00449B7E /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3203916C234E4D7A00449B7E /* LaunchScreen.storyboard */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXFileReference section */ 20 | 3203915E234E4D7800449B7E /* TextDecompose.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TextDecompose.app; sourceTree = BUILT_PRODUCTS_DIR; }; 21 | 32039161234E4D7800449B7E /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 22 | 32039163234E4D7800449B7E /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; 23 | 32039165234E4D7800449B7E /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 24 | 32039168234E4D7800449B7E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 25 | 3203916A234E4D7A00449B7E /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 26 | 3203916D234E4D7A00449B7E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 27 | 3203916F234E4D7A00449B7E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 28 | 969F72B520AC69B352E1C2AE /* Pods-TextDecompose.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TextDecompose.debug.xcconfig"; path = "Target Support Files/Pods-TextDecompose/Pods-TextDecompose.debug.xcconfig"; sourceTree = ""; }; 29 | A3098CBC0AB85AB6ACCF4626 /* Pods-TextDecompose.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TextDecompose.release.xcconfig"; path = "Target Support Files/Pods-TextDecompose/Pods-TextDecompose.release.xcconfig"; sourceTree = ""; }; 30 | E666E8D120942706C134605B /* libPods-TextDecompose.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-TextDecompose.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 31 | /* End PBXFileReference section */ 32 | 33 | /* Begin PBXFrameworksBuildPhase section */ 34 | 3203915B234E4D7800449B7E /* Frameworks */ = { 35 | isa = PBXFrameworksBuildPhase; 36 | buildActionMask = 2147483647; 37 | files = ( 38 | 15B95874AD6FD3794E204186 /* libPods-TextDecompose.a in Frameworks */, 39 | ); 40 | runOnlyForDeploymentPostprocessing = 0; 41 | }; 42 | /* End PBXFrameworksBuildPhase section */ 43 | 44 | /* Begin PBXGroup section */ 45 | 32039155234E4D7800449B7E = { 46 | isa = PBXGroup; 47 | children = ( 48 | 32039160234E4D7800449B7E /* TextDecompose */, 49 | 3203915F234E4D7800449B7E /* Products */, 50 | 5ABD16147F39F43254D2897E /* Pods */, 51 | 97C68AA34C85D603CE6F4662 /* Frameworks */, 52 | ); 53 | sourceTree = ""; 54 | }; 55 | 3203915F234E4D7800449B7E /* Products */ = { 56 | isa = PBXGroup; 57 | children = ( 58 | 3203915E234E4D7800449B7E /* TextDecompose.app */, 59 | ); 60 | name = Products; 61 | sourceTree = ""; 62 | }; 63 | 32039160234E4D7800449B7E /* TextDecompose */ = { 64 | isa = PBXGroup; 65 | children = ( 66 | 32039161234E4D7800449B7E /* AppDelegate.swift */, 67 | 32039163234E4D7800449B7E /* SceneDelegate.swift */, 68 | 32039165234E4D7800449B7E /* ViewController.swift */, 69 | 32039167234E4D7800449B7E /* Main.storyboard */, 70 | 3203916A234E4D7A00449B7E /* Assets.xcassets */, 71 | 3203916C234E4D7A00449B7E /* LaunchScreen.storyboard */, 72 | 3203916F234E4D7A00449B7E /* Info.plist */, 73 | ); 74 | path = TextDecompose; 75 | sourceTree = ""; 76 | }; 77 | 5ABD16147F39F43254D2897E /* Pods */ = { 78 | isa = PBXGroup; 79 | children = ( 80 | 969F72B520AC69B352E1C2AE /* Pods-TextDecompose.debug.xcconfig */, 81 | A3098CBC0AB85AB6ACCF4626 /* Pods-TextDecompose.release.xcconfig */, 82 | ); 83 | name = Pods; 84 | path = Pods; 85 | sourceTree = ""; 86 | }; 87 | 97C68AA34C85D603CE6F4662 /* Frameworks */ = { 88 | isa = PBXGroup; 89 | children = ( 90 | E666E8D120942706C134605B /* libPods-TextDecompose.a */, 91 | ); 92 | name = Frameworks; 93 | sourceTree = ""; 94 | }; 95 | /* End PBXGroup section */ 96 | 97 | /* Begin PBXNativeTarget section */ 98 | 3203915D234E4D7800449B7E /* TextDecompose */ = { 99 | isa = PBXNativeTarget; 100 | buildConfigurationList = 32039172234E4D7A00449B7E /* Build configuration list for PBXNativeTarget "TextDecompose" */; 101 | buildPhases = ( 102 | B8D7EC7FC41E957F4FC16B66 /* [CP] Check Pods Manifest.lock */, 103 | 3203915A234E4D7800449B7E /* Sources */, 104 | 3203915B234E4D7800449B7E /* Frameworks */, 105 | 3203915C234E4D7800449B7E /* Resources */, 106 | ); 107 | buildRules = ( 108 | ); 109 | dependencies = ( 110 | ); 111 | name = TextDecompose; 112 | productName = TextDecompose; 113 | productReference = 3203915E234E4D7800449B7E /* TextDecompose.app */; 114 | productType = "com.apple.product-type.application"; 115 | }; 116 | /* End PBXNativeTarget section */ 117 | 118 | /* Begin PBXProject section */ 119 | 32039156234E4D7800449B7E /* Project object */ = { 120 | isa = PBXProject; 121 | attributes = { 122 | LastSwiftUpdateCheck = 1100; 123 | LastUpgradeCheck = 1100; 124 | TargetAttributes = { 125 | 3203915D234E4D7800449B7E = { 126 | CreatedOnToolsVersion = 11.0; 127 | }; 128 | }; 129 | }; 130 | buildConfigurationList = 32039159234E4D7800449B7E /* Build configuration list for PBXProject "TextDecompose" */; 131 | compatibilityVersion = "Xcode 9.3"; 132 | developmentRegion = en; 133 | hasScannedForEncodings = 0; 134 | knownRegions = ( 135 | en, 136 | Base, 137 | ); 138 | mainGroup = 32039155234E4D7800449B7E; 139 | productRefGroup = 3203915F234E4D7800449B7E /* Products */; 140 | projectDirPath = ""; 141 | projectRoot = ""; 142 | targets = ( 143 | 3203915D234E4D7800449B7E /* TextDecompose */, 144 | ); 145 | }; 146 | /* End PBXProject section */ 147 | 148 | /* Begin PBXResourcesBuildPhase section */ 149 | 3203915C234E4D7800449B7E /* Resources */ = { 150 | isa = PBXResourcesBuildPhase; 151 | buildActionMask = 2147483647; 152 | files = ( 153 | 3203916E234E4D7A00449B7E /* LaunchScreen.storyboard in Resources */, 154 | 3203916B234E4D7A00449B7E /* Assets.xcassets in Resources */, 155 | 32039169234E4D7800449B7E /* Main.storyboard in Resources */, 156 | ); 157 | runOnlyForDeploymentPostprocessing = 0; 158 | }; 159 | /* End PBXResourcesBuildPhase section */ 160 | 161 | /* Begin PBXShellScriptBuildPhase section */ 162 | B8D7EC7FC41E957F4FC16B66 /* [CP] Check Pods Manifest.lock */ = { 163 | isa = PBXShellScriptBuildPhase; 164 | buildActionMask = 2147483647; 165 | files = ( 166 | ); 167 | inputFileListPaths = ( 168 | ); 169 | inputPaths = ( 170 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 171 | "${PODS_ROOT}/Manifest.lock", 172 | ); 173 | name = "[CP] Check Pods Manifest.lock"; 174 | outputFileListPaths = ( 175 | ); 176 | outputPaths = ( 177 | "$(DERIVED_FILE_DIR)/Pods-TextDecompose-checkManifestLockResult.txt", 178 | ); 179 | runOnlyForDeploymentPostprocessing = 0; 180 | shellPath = /bin/sh; 181 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 182 | showEnvVarsInLog = 0; 183 | }; 184 | /* End PBXShellScriptBuildPhase section */ 185 | 186 | /* Begin PBXSourcesBuildPhase section */ 187 | 3203915A234E4D7800449B7E /* Sources */ = { 188 | isa = PBXSourcesBuildPhase; 189 | buildActionMask = 2147483647; 190 | files = ( 191 | 32039166234E4D7800449B7E /* ViewController.swift in Sources */, 192 | 32039162234E4D7800449B7E /* AppDelegate.swift in Sources */, 193 | 32039164234E4D7800449B7E /* SceneDelegate.swift in Sources */, 194 | ); 195 | runOnlyForDeploymentPostprocessing = 0; 196 | }; 197 | /* End PBXSourcesBuildPhase section */ 198 | 199 | /* Begin PBXVariantGroup section */ 200 | 32039167234E4D7800449B7E /* Main.storyboard */ = { 201 | isa = PBXVariantGroup; 202 | children = ( 203 | 32039168234E4D7800449B7E /* Base */, 204 | ); 205 | name = Main.storyboard; 206 | sourceTree = ""; 207 | }; 208 | 3203916C234E4D7A00449B7E /* LaunchScreen.storyboard */ = { 209 | isa = PBXVariantGroup; 210 | children = ( 211 | 3203916D234E4D7A00449B7E /* Base */, 212 | ); 213 | name = LaunchScreen.storyboard; 214 | sourceTree = ""; 215 | }; 216 | /* End PBXVariantGroup section */ 217 | 218 | /* Begin XCBuildConfiguration section */ 219 | 32039170234E4D7A00449B7E /* Debug */ = { 220 | isa = XCBuildConfiguration; 221 | buildSettings = { 222 | ALWAYS_SEARCH_USER_PATHS = NO; 223 | CLANG_ANALYZER_NONNULL = YES; 224 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 225 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 226 | CLANG_CXX_LIBRARY = "libc++"; 227 | CLANG_ENABLE_MODULES = YES; 228 | CLANG_ENABLE_OBJC_ARC = YES; 229 | CLANG_ENABLE_OBJC_WEAK = YES; 230 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 231 | CLANG_WARN_BOOL_CONVERSION = YES; 232 | CLANG_WARN_COMMA = YES; 233 | CLANG_WARN_CONSTANT_CONVERSION = YES; 234 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 235 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 236 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 237 | CLANG_WARN_EMPTY_BODY = YES; 238 | CLANG_WARN_ENUM_CONVERSION = YES; 239 | CLANG_WARN_INFINITE_RECURSION = YES; 240 | CLANG_WARN_INT_CONVERSION = YES; 241 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 242 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 243 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 244 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 245 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 246 | CLANG_WARN_STRICT_PROTOTYPES = YES; 247 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 248 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 249 | CLANG_WARN_UNREACHABLE_CODE = YES; 250 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 251 | COPY_PHASE_STRIP = NO; 252 | DEBUG_INFORMATION_FORMAT = dwarf; 253 | ENABLE_STRICT_OBJC_MSGSEND = YES; 254 | ENABLE_TESTABILITY = YES; 255 | GCC_C_LANGUAGE_STANDARD = gnu11; 256 | GCC_DYNAMIC_NO_PIC = NO; 257 | GCC_NO_COMMON_BLOCKS = YES; 258 | GCC_OPTIMIZATION_LEVEL = 0; 259 | GCC_PREPROCESSOR_DEFINITIONS = ( 260 | "DEBUG=1", 261 | "$(inherited)", 262 | ); 263 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 264 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 265 | GCC_WARN_UNDECLARED_SELECTOR = YES; 266 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 267 | GCC_WARN_UNUSED_FUNCTION = YES; 268 | GCC_WARN_UNUSED_VARIABLE = YES; 269 | IPHONEOS_DEPLOYMENT_TARGET = 13.0; 270 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 271 | MTL_FAST_MATH = YES; 272 | ONLY_ACTIVE_ARCH = YES; 273 | SDKROOT = iphoneos; 274 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 275 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 276 | }; 277 | name = Debug; 278 | }; 279 | 32039171234E4D7A00449B7E /* Release */ = { 280 | isa = XCBuildConfiguration; 281 | buildSettings = { 282 | ALWAYS_SEARCH_USER_PATHS = NO; 283 | CLANG_ANALYZER_NONNULL = YES; 284 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 285 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 286 | CLANG_CXX_LIBRARY = "libc++"; 287 | CLANG_ENABLE_MODULES = YES; 288 | CLANG_ENABLE_OBJC_ARC = YES; 289 | CLANG_ENABLE_OBJC_WEAK = YES; 290 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 291 | CLANG_WARN_BOOL_CONVERSION = YES; 292 | CLANG_WARN_COMMA = YES; 293 | CLANG_WARN_CONSTANT_CONVERSION = YES; 294 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 295 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 296 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 297 | CLANG_WARN_EMPTY_BODY = YES; 298 | CLANG_WARN_ENUM_CONVERSION = YES; 299 | CLANG_WARN_INFINITE_RECURSION = YES; 300 | CLANG_WARN_INT_CONVERSION = YES; 301 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 302 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 303 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 304 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 305 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 306 | CLANG_WARN_STRICT_PROTOTYPES = YES; 307 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 308 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 309 | CLANG_WARN_UNREACHABLE_CODE = YES; 310 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 311 | COPY_PHASE_STRIP = NO; 312 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 313 | ENABLE_NS_ASSERTIONS = NO; 314 | ENABLE_STRICT_OBJC_MSGSEND = YES; 315 | GCC_C_LANGUAGE_STANDARD = gnu11; 316 | GCC_NO_COMMON_BLOCKS = YES; 317 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 318 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 319 | GCC_WARN_UNDECLARED_SELECTOR = YES; 320 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 321 | GCC_WARN_UNUSED_FUNCTION = YES; 322 | GCC_WARN_UNUSED_VARIABLE = YES; 323 | IPHONEOS_DEPLOYMENT_TARGET = 13.0; 324 | MTL_ENABLE_DEBUG_INFO = NO; 325 | MTL_FAST_MATH = YES; 326 | SDKROOT = iphoneos; 327 | SWIFT_COMPILATION_MODE = wholemodule; 328 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 329 | VALIDATE_PRODUCT = YES; 330 | }; 331 | name = Release; 332 | }; 333 | 32039173234E4D7A00449B7E /* Debug */ = { 334 | isa = XCBuildConfiguration; 335 | baseConfigurationReference = 969F72B520AC69B352E1C2AE /* Pods-TextDecompose.debug.xcconfig */; 336 | buildSettings = { 337 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 338 | CODE_SIGN_STYLE = Automatic; 339 | DEVELOPMENT_TEAM = HT3GA3Z42X; 340 | INFOPLIST_FILE = TextDecompose/Info.plist; 341 | LD_RUNPATH_SEARCH_PATHS = ( 342 | "$(inherited)", 343 | "@executable_path/Frameworks", 344 | ); 345 | PRODUCT_BUNDLE_IDENTIFIER = cat.thepirate.TextDecompose; 346 | PRODUCT_NAME = "$(TARGET_NAME)"; 347 | SWIFT_VERSION = 5.0; 348 | TARGETED_DEVICE_FAMILY = "1,2"; 349 | }; 350 | name = Debug; 351 | }; 352 | 32039174234E4D7A00449B7E /* Release */ = { 353 | isa = XCBuildConfiguration; 354 | baseConfigurationReference = A3098CBC0AB85AB6ACCF4626 /* Pods-TextDecompose.release.xcconfig */; 355 | buildSettings = { 356 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 357 | CODE_SIGN_STYLE = Automatic; 358 | DEVELOPMENT_TEAM = HT3GA3Z42X; 359 | INFOPLIST_FILE = TextDecompose/Info.plist; 360 | LD_RUNPATH_SEARCH_PATHS = ( 361 | "$(inherited)", 362 | "@executable_path/Frameworks", 363 | ); 364 | PRODUCT_BUNDLE_IDENTIFIER = cat.thepirate.TextDecompose; 365 | PRODUCT_NAME = "$(TARGET_NAME)"; 366 | SWIFT_VERSION = 5.0; 367 | TARGETED_DEVICE_FAMILY = "1,2"; 368 | }; 369 | name = Release; 370 | }; 371 | /* End XCBuildConfiguration section */ 372 | 373 | /* Begin XCConfigurationList section */ 374 | 32039159234E4D7800449B7E /* Build configuration list for PBXProject "TextDecompose" */ = { 375 | isa = XCConfigurationList; 376 | buildConfigurations = ( 377 | 32039170234E4D7A00449B7E /* Debug */, 378 | 32039171234E4D7A00449B7E /* Release */, 379 | ); 380 | defaultConfigurationIsVisible = 0; 381 | defaultConfigurationName = Release; 382 | }; 383 | 32039172234E4D7A00449B7E /* Build configuration list for PBXNativeTarget "TextDecompose" */ = { 384 | isa = XCConfigurationList; 385 | buildConfigurations = ( 386 | 32039173234E4D7A00449B7E /* Debug */, 387 | 32039174234E4D7A00449B7E /* Release */, 388 | ); 389 | defaultConfigurationIsVisible = 0; 390 | defaultConfigurationName = Release; 391 | }; 392 | /* End XCConfigurationList section */ 393 | }; 394 | rootObject = 32039156234E4D7800449B7E /* Project object */; 395 | } 396 | -------------------------------------------------------------------------------- /Samples/TextDecompose/TextDecompose.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Samples/TextDecompose/TextDecompose.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Samples/TextDecompose/TextDecompose/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // TextDecompose 4 | // 5 | // Created by Mateusz Malczak on 09/10/2019. 6 | // 7 | 8 | import UIKit 9 | 10 | @UIApplicationMain 11 | class AppDelegate: UIResponder, UIApplicationDelegate { 12 | 13 | 14 | 15 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 16 | // Override point for customization after application launch. 17 | return true 18 | } 19 | 20 | // MARK: UISceneSession Lifecycle 21 | 22 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { 23 | // Called when a new scene session is being created. 24 | // Use this method to select a configuration to create the new scene with. 25 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) 26 | } 27 | 28 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { 29 | // Called when the user discards a scene session. 30 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. 31 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return. 32 | } 33 | 34 | 35 | } 36 | 37 | -------------------------------------------------------------------------------- /Samples/TextDecompose/TextDecompose/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /Samples/TextDecompose/TextDecompose/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Samples/TextDecompose/TextDecompose/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Samples/TextDecompose/TextDecompose/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | Lorem Ipsum is simply dummy 46 | 47 | 48 | 49 | 50 | 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 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 115 | 121 | 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 | -------------------------------------------------------------------------------- /Samples/TextDecompose/TextDecompose/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UIApplicationSceneManifest 24 | 25 | UIApplicationSupportsMultipleScenes 26 | 27 | UISceneConfigurations 28 | 29 | UIWindowSceneSessionRoleApplication 30 | 31 | 32 | UISceneConfigurationName 33 | Default Configuration 34 | UISceneDelegateClassName 35 | $(PRODUCT_MODULE_NAME).SceneDelegate 36 | UISceneStoryboardFile 37 | Main 38 | 39 | 40 | 41 | 42 | UILaunchStoryboardName 43 | LaunchScreen 44 | UIMainStoryboardFile 45 | Main 46 | UIRequiredDeviceCapabilities 47 | 48 | armv7 49 | 50 | UISupportedInterfaceOrientations 51 | 52 | UIInterfaceOrientationPortrait 53 | UIInterfaceOrientationLandscapeLeft 54 | UIInterfaceOrientationLandscapeRight 55 | 56 | UISupportedInterfaceOrientations~ipad 57 | 58 | UIInterfaceOrientationPortrait 59 | UIInterfaceOrientationPortraitUpsideDown 60 | UIInterfaceOrientationLandscapeLeft 61 | UIInterfaceOrientationLandscapeRight 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /Samples/TextDecompose/TextDecompose/SceneDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.swift 3 | // TextDecompose 4 | // 5 | // Created by Mateusz Malczak on 09/10/2019. 6 | // 7 | 8 | import UIKit 9 | 10 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { 11 | 12 | var window: UIWindow? 13 | 14 | 15 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 16 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. 17 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. 18 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). 19 | guard let _ = (scene as? UIWindowScene) else { return } 20 | } 21 | 22 | func sceneDidDisconnect(_ scene: UIScene) { 23 | // Called as the scene is being released by the system. 24 | // This occurs shortly after the scene enters the background, or when its session is discarded. 25 | // Release any resources associated with this scene that can be re-created the next time the scene connects. 26 | // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead). 27 | } 28 | 29 | func sceneDidBecomeActive(_ scene: UIScene) { 30 | // Called when the scene has moved from an inactive state to an active state. 31 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. 32 | } 33 | 34 | func sceneWillResignActive(_ scene: UIScene) { 35 | // Called when the scene will move from an active state to an inactive state. 36 | // This may occur due to temporary interruptions (ex. an incoming phone call). 37 | } 38 | 39 | func sceneWillEnterForeground(_ scene: UIScene) { 40 | // Called as the scene transitions from the background to the foreground. 41 | // Use this method to undo the changes made on entering the background. 42 | } 43 | 44 | func sceneDidEnterBackground(_ scene: UIScene) { 45 | // Called as the scene transitions from the foreground to the background. 46 | // Use this method to save data, release shared resources, and store enough scene-specific state information 47 | // to restore the scene back to its current state. 48 | } 49 | 50 | 51 | } 52 | 53 | -------------------------------------------------------------------------------- /Samples/TextDecompose/TextDecompose/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // TextDecompose 4 | // 5 | // Created by Mateusz Malczak on 09/10/2019. 6 | // 7 | 8 | import UIKit 9 | import TextPaths 10 | 11 | class ViewController: UIViewController { 12 | 13 | @IBOutlet weak var textView: UITextView! 14 | 15 | @IBOutlet weak var container: UIView! 16 | 17 | var drawLineBounds = true 18 | 19 | var drawTextLineBounds = true 20 | 21 | var drawGlyphsBounds = true 22 | 23 | @IBAction func switchLineLeading(_ sender: UISwitch) { 24 | drawLineBounds = !drawLineBounds 25 | decomposeText() 26 | } 27 | 28 | @IBAction func switchLineText(_ sender: UISwitch) { 29 | drawTextLineBounds = !drawTextLineBounds 30 | decomposeText() 31 | } 32 | 33 | @IBAction func switchGlyphs(_ sender: UISwitch) { 34 | drawGlyphsBounds = !drawGlyphsBounds 35 | decomposeText() 36 | } 37 | 38 | override func viewDidLoad() { 39 | super.viewDidLoad() 40 | decomposeText() 41 | } 42 | 43 | func decomposeText() { 44 | let hardcodedTextViewMargins: CGFloat = 12 45 | let bounds = CGSize(width: textView.frame.width - hardcodedTextViewMargins, 46 | height: CGFloat.greatestFiniteMagnitude) 47 | guard let textPaths = textView.attributedText.getTextPath(InBounds: bounds, 48 | withAttributes: true, 49 | withPath: true) else { 50 | return 51 | } 52 | 53 | let textBounds = textPaths.composedBounds 54 | 55 | let offset = CGPoint( 56 | x: textBounds.width < container.bounds.width ? (container.bounds.width - textBounds.width) * 0.5 : 0, 57 | y: textBounds.height < container.bounds.height ? (container.bounds.height - textBounds.height) * 0.5 : 0 58 | ) 59 | 60 | let layer = container.layer 61 | 62 | // cleanup 63 | layer 64 | .sublayers? 65 | .reversed() 66 | .forEach { $0.removeFromSuperlayer() } 67 | 68 | let container = CAShapeLayer() 69 | container.bounds = textBounds 70 | container.anchorPoint = .zero 71 | container.position = offset 72 | layer.addSublayer(container); 73 | 74 | // draw composed text path 75 | let textShape = CAShapeLayer() 76 | textShape.anchorPoint = .zero 77 | textShape.bounds = textBounds 78 | textShape.path = textPaths.composedPath! 79 | textShape.fillColor = UIColor.darkText.cgColor 80 | container.addSublayer(textShape) 81 | 82 | for frame in textPaths.frames { 83 | container.addSublayer(rect(frame.path.boundingBoxOfPath, .red)) 84 | 85 | var lineOffsetY: CGFloat = 0.0 86 | 87 | for line in frame.lines { 88 | let lineBounds = line.lineBounds 89 | let textBounds = line.textBounds 90 | 91 | if drawLineBounds { 92 | let shape = rect(lineBounds, .blue) 93 | shape.position = CGPoint(x: lineBounds.origin.x, 94 | y: lineOffsetY) 95 | container.addSublayer(shape) 96 | } 97 | 98 | if drawTextLineBounds { 99 | let textShape = rect(textBounds, .purple) 100 | textShape.position = CGPoint(x: lineBounds.origin.x + textBounds.origin.x, 101 | y: lineOffsetY + lineBounds.origin.x) 102 | container.addSublayer(textShape) 103 | } 104 | 105 | if drawGlyphsBounds { 106 | line.enumerateGlyphs { (_, glyph) in 107 | let glyphBounds = glyph.path.boundingBoxOfPath 108 | let bounds = CGRect(origin: glyph.position, size: glyphBounds.size) 109 | let shape = rect(bounds, .green) 110 | shape.position = CGPoint(x: glyph.position.x, 111 | y: glyph.position.y) 112 | container.addSublayer(shape) 113 | } 114 | } 115 | 116 | // move to next line (we keep line bounds in own coordinate space) 117 | lineOffsetY = lineOffsetY + lineBounds.height 118 | } 119 | } 120 | 121 | } 122 | 123 | func rect(_ rect: CGRect, _ color: UIColor) -> CAShapeLayer { 124 | let shape = CAShapeLayer() 125 | let bounds = CGRect(origin: .zero, size: rect.size) 126 | let origin = rect.origin 127 | shape.bounds = bounds 128 | shape.strokeColor = color.cgColor 129 | shape.fillColor = nil 130 | shape.anchorPoint = .zero 131 | shape.path = UIBezierPath(rect: bounds).cgPath 132 | shape.position = origin 133 | return shape 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /Samples/TextToSVG/.gitignore: -------------------------------------------------------------------------------- 1 | Pods/ 2 | Podfile.lock 3 | *.xcworkspace 4 | .DS_Store 5 | 6 | ### Xcode ### 7 | # Xcode 8 | # 9 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 10 | 11 | ## User settings 12 | xcuserdata/ 13 | 14 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 15 | *.xcscmblueprint 16 | *.xccheckout 17 | 18 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 19 | build/ 20 | DerivedData/ 21 | *.moved-aside 22 | *.pbxuser 23 | !default.pbxuser 24 | *.mode1v3 25 | !default.mode1v3 26 | *.mode2v3 27 | !default.mode2v3 28 | *.perspectivev3 29 | !default.perspectivev3 30 | 31 | ## Xcode Patch 32 | *.xcodeproj/* 33 | !*.xcodeproj/project.pbxproj 34 | !*.xcodeproj/xcshareddata/ 35 | !*.xcworkspace/contents.xcworkspacedata 36 | /*.gcno 37 | 38 | ### Xcode Patch ### 39 | **/xcshareddata/WorkspaceSettings.xcsettings 40 | -------------------------------------------------------------------------------- /Samples/TextToSVG/Podfile: -------------------------------------------------------------------------------- 1 | target 'TextToSVG' 2 | pod 'TextPaths', :path => './../..' 3 | -------------------------------------------------------------------------------- /Samples/TextToSVG/TextToSVG.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 324B6032234D3C9F009FA70D /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 324B6031234D3C9F009FA70D /* AppDelegate.swift */; }; 11 | 324B6034234D3C9F009FA70D /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 324B6033234D3C9F009FA70D /* SceneDelegate.swift */; }; 12 | 324B6036234D3C9F009FA70D /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 324B6035234D3C9F009FA70D /* ViewController.swift */; }; 13 | 324B6039234D3C9F009FA70D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 324B6037234D3C9F009FA70D /* Main.storyboard */; }; 14 | 324B603B234D3CA0009FA70D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 324B603A234D3CA0009FA70D /* Assets.xcassets */; }; 15 | 324B603E234D3CA0009FA70D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 324B603C234D3CA0009FA70D /* LaunchScreen.storyboard */; }; 16 | 8BA963CE2B654EAD49D1C075 /* libPods-TextToSVG.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7FCD469CC2E5397EEEA52197 /* libPods-TextToSVG.a */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXFileReference section */ 20 | 10508FD6454EC7889AD3B05A /* Pods-TextToSVG.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TextToSVG.release.xcconfig"; path = "Target Support Files/Pods-TextToSVG/Pods-TextToSVG.release.xcconfig"; sourceTree = ""; }; 21 | 324B602E234D3C9F009FA70D /* TextToSVG.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TextToSVG.app; sourceTree = BUILT_PRODUCTS_DIR; }; 22 | 324B6031234D3C9F009FA70D /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 23 | 324B6033234D3C9F009FA70D /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; 24 | 324B6035234D3C9F009FA70D /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 25 | 324B6038234D3C9F009FA70D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 26 | 324B603A234D3CA0009FA70D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 27 | 324B603D234D3CA0009FA70D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 28 | 324B603F234D3CA0009FA70D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 29 | 7FCD469CC2E5397EEEA52197 /* libPods-TextToSVG.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-TextToSVG.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 30 | E6FED3283B3AF78AB183D784 /* Pods-TextToSVG.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TextToSVG.debug.xcconfig"; path = "Target Support Files/Pods-TextToSVG/Pods-TextToSVG.debug.xcconfig"; sourceTree = ""; }; 31 | /* End PBXFileReference section */ 32 | 33 | /* Begin PBXFrameworksBuildPhase section */ 34 | 324B602B234D3C9F009FA70D /* Frameworks */ = { 35 | isa = PBXFrameworksBuildPhase; 36 | buildActionMask = 2147483647; 37 | files = ( 38 | 8BA963CE2B654EAD49D1C075 /* libPods-TextToSVG.a in Frameworks */, 39 | ); 40 | runOnlyForDeploymentPostprocessing = 0; 41 | }; 42 | /* End PBXFrameworksBuildPhase section */ 43 | 44 | /* Begin PBXGroup section */ 45 | 324B6025234D3C9F009FA70D = { 46 | isa = PBXGroup; 47 | children = ( 48 | 324B6030234D3C9F009FA70D /* TextToSVG */, 49 | 324B602F234D3C9F009FA70D /* Products */, 50 | 37CE83FF7C8A5A86D1C30D90 /* Pods */, 51 | 514369197E1D2FCF5CFAEBA2 /* Frameworks */, 52 | ); 53 | sourceTree = ""; 54 | }; 55 | 324B602F234D3C9F009FA70D /* Products */ = { 56 | isa = PBXGroup; 57 | children = ( 58 | 324B602E234D3C9F009FA70D /* TextToSVG.app */, 59 | ); 60 | name = Products; 61 | sourceTree = ""; 62 | }; 63 | 324B6030234D3C9F009FA70D /* TextToSVG */ = { 64 | isa = PBXGroup; 65 | children = ( 66 | 324B6031234D3C9F009FA70D /* AppDelegate.swift */, 67 | 324B6033234D3C9F009FA70D /* SceneDelegate.swift */, 68 | 324B6035234D3C9F009FA70D /* ViewController.swift */, 69 | 324B6037234D3C9F009FA70D /* Main.storyboard */, 70 | 324B603A234D3CA0009FA70D /* Assets.xcassets */, 71 | 324B603C234D3CA0009FA70D /* LaunchScreen.storyboard */, 72 | 324B603F234D3CA0009FA70D /* Info.plist */, 73 | ); 74 | path = TextToSVG; 75 | sourceTree = ""; 76 | }; 77 | 37CE83FF7C8A5A86D1C30D90 /* Pods */ = { 78 | isa = PBXGroup; 79 | children = ( 80 | E6FED3283B3AF78AB183D784 /* Pods-TextToSVG.debug.xcconfig */, 81 | 10508FD6454EC7889AD3B05A /* Pods-TextToSVG.release.xcconfig */, 82 | ); 83 | name = Pods; 84 | path = Pods; 85 | sourceTree = ""; 86 | }; 87 | 514369197E1D2FCF5CFAEBA2 /* Frameworks */ = { 88 | isa = PBXGroup; 89 | children = ( 90 | 7FCD469CC2E5397EEEA52197 /* libPods-TextToSVG.a */, 91 | ); 92 | name = Frameworks; 93 | sourceTree = ""; 94 | }; 95 | /* End PBXGroup section */ 96 | 97 | /* Begin PBXNativeTarget section */ 98 | 324B602D234D3C9F009FA70D /* TextToSVG */ = { 99 | isa = PBXNativeTarget; 100 | buildConfigurationList = 324B6042234D3CA0009FA70D /* Build configuration list for PBXNativeTarget "TextToSVG" */; 101 | buildPhases = ( 102 | 91B92085F1662A96B3FC5A8E /* [CP] Check Pods Manifest.lock */, 103 | 324B602A234D3C9F009FA70D /* Sources */, 104 | 324B602B234D3C9F009FA70D /* Frameworks */, 105 | 324B602C234D3C9F009FA70D /* Resources */, 106 | ); 107 | buildRules = ( 108 | ); 109 | dependencies = ( 110 | ); 111 | name = TextToSVG; 112 | productName = TextToSVG; 113 | productReference = 324B602E234D3C9F009FA70D /* TextToSVG.app */; 114 | productType = "com.apple.product-type.application"; 115 | }; 116 | /* End PBXNativeTarget section */ 117 | 118 | /* Begin PBXProject section */ 119 | 324B6026234D3C9F009FA70D /* Project object */ = { 120 | isa = PBXProject; 121 | attributes = { 122 | LastSwiftUpdateCheck = 1100; 123 | LastUpgradeCheck = 1100; 124 | TargetAttributes = { 125 | 324B602D234D3C9F009FA70D = { 126 | CreatedOnToolsVersion = 11.0; 127 | }; 128 | }; 129 | }; 130 | buildConfigurationList = 324B6029234D3C9F009FA70D /* Build configuration list for PBXProject "TextToSVG" */; 131 | compatibilityVersion = "Xcode 9.3"; 132 | developmentRegion = en; 133 | hasScannedForEncodings = 0; 134 | knownRegions = ( 135 | en, 136 | Base, 137 | ); 138 | mainGroup = 324B6025234D3C9F009FA70D; 139 | productRefGroup = 324B602F234D3C9F009FA70D /* Products */; 140 | projectDirPath = ""; 141 | projectRoot = ""; 142 | targets = ( 143 | 324B602D234D3C9F009FA70D /* TextToSVG */, 144 | ); 145 | }; 146 | /* End PBXProject section */ 147 | 148 | /* Begin PBXResourcesBuildPhase section */ 149 | 324B602C234D3C9F009FA70D /* Resources */ = { 150 | isa = PBXResourcesBuildPhase; 151 | buildActionMask = 2147483647; 152 | files = ( 153 | 324B603E234D3CA0009FA70D /* LaunchScreen.storyboard in Resources */, 154 | 324B603B234D3CA0009FA70D /* Assets.xcassets in Resources */, 155 | 324B6039234D3C9F009FA70D /* Main.storyboard in Resources */, 156 | ); 157 | runOnlyForDeploymentPostprocessing = 0; 158 | }; 159 | /* End PBXResourcesBuildPhase section */ 160 | 161 | /* Begin PBXShellScriptBuildPhase section */ 162 | 91B92085F1662A96B3FC5A8E /* [CP] Check Pods Manifest.lock */ = { 163 | isa = PBXShellScriptBuildPhase; 164 | buildActionMask = 2147483647; 165 | files = ( 166 | ); 167 | inputFileListPaths = ( 168 | ); 169 | inputPaths = ( 170 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 171 | "${PODS_ROOT}/Manifest.lock", 172 | ); 173 | name = "[CP] Check Pods Manifest.lock"; 174 | outputFileListPaths = ( 175 | ); 176 | outputPaths = ( 177 | "$(DERIVED_FILE_DIR)/Pods-TextToSVG-checkManifestLockResult.txt", 178 | ); 179 | runOnlyForDeploymentPostprocessing = 0; 180 | shellPath = /bin/sh; 181 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 182 | showEnvVarsInLog = 0; 183 | }; 184 | /* End PBXShellScriptBuildPhase section */ 185 | 186 | /* Begin PBXSourcesBuildPhase section */ 187 | 324B602A234D3C9F009FA70D /* Sources */ = { 188 | isa = PBXSourcesBuildPhase; 189 | buildActionMask = 2147483647; 190 | files = ( 191 | 324B6036234D3C9F009FA70D /* ViewController.swift in Sources */, 192 | 324B6032234D3C9F009FA70D /* AppDelegate.swift in Sources */, 193 | 324B6034234D3C9F009FA70D /* SceneDelegate.swift in Sources */, 194 | ); 195 | runOnlyForDeploymentPostprocessing = 0; 196 | }; 197 | /* End PBXSourcesBuildPhase section */ 198 | 199 | /* Begin PBXVariantGroup section */ 200 | 324B6037234D3C9F009FA70D /* Main.storyboard */ = { 201 | isa = PBXVariantGroup; 202 | children = ( 203 | 324B6038234D3C9F009FA70D /* Base */, 204 | ); 205 | name = Main.storyboard; 206 | sourceTree = ""; 207 | }; 208 | 324B603C234D3CA0009FA70D /* LaunchScreen.storyboard */ = { 209 | isa = PBXVariantGroup; 210 | children = ( 211 | 324B603D234D3CA0009FA70D /* Base */, 212 | ); 213 | name = LaunchScreen.storyboard; 214 | sourceTree = ""; 215 | }; 216 | /* End PBXVariantGroup section */ 217 | 218 | /* Begin XCBuildConfiguration section */ 219 | 324B6040234D3CA0009FA70D /* Debug */ = { 220 | isa = XCBuildConfiguration; 221 | buildSettings = { 222 | ALWAYS_SEARCH_USER_PATHS = NO; 223 | CLANG_ANALYZER_NONNULL = YES; 224 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 225 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 226 | CLANG_CXX_LIBRARY = "libc++"; 227 | CLANG_ENABLE_MODULES = YES; 228 | CLANG_ENABLE_OBJC_ARC = YES; 229 | CLANG_ENABLE_OBJC_WEAK = YES; 230 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 231 | CLANG_WARN_BOOL_CONVERSION = YES; 232 | CLANG_WARN_COMMA = YES; 233 | CLANG_WARN_CONSTANT_CONVERSION = YES; 234 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 235 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 236 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 237 | CLANG_WARN_EMPTY_BODY = YES; 238 | CLANG_WARN_ENUM_CONVERSION = YES; 239 | CLANG_WARN_INFINITE_RECURSION = YES; 240 | CLANG_WARN_INT_CONVERSION = YES; 241 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 242 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 243 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 244 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 245 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 246 | CLANG_WARN_STRICT_PROTOTYPES = YES; 247 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 248 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 249 | CLANG_WARN_UNREACHABLE_CODE = YES; 250 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 251 | COPY_PHASE_STRIP = NO; 252 | DEBUG_INFORMATION_FORMAT = dwarf; 253 | ENABLE_STRICT_OBJC_MSGSEND = YES; 254 | ENABLE_TESTABILITY = YES; 255 | GCC_C_LANGUAGE_STANDARD = gnu11; 256 | GCC_DYNAMIC_NO_PIC = NO; 257 | GCC_NO_COMMON_BLOCKS = YES; 258 | GCC_OPTIMIZATION_LEVEL = 0; 259 | GCC_PREPROCESSOR_DEFINITIONS = ( 260 | "DEBUG=1", 261 | "$(inherited)", 262 | ); 263 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 264 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 265 | GCC_WARN_UNDECLARED_SELECTOR = YES; 266 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 267 | GCC_WARN_UNUSED_FUNCTION = YES; 268 | GCC_WARN_UNUSED_VARIABLE = YES; 269 | IPHONEOS_DEPLOYMENT_TARGET = 13.0; 270 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 271 | MTL_FAST_MATH = YES; 272 | ONLY_ACTIVE_ARCH = YES; 273 | SDKROOT = iphoneos; 274 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 275 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 276 | }; 277 | name = Debug; 278 | }; 279 | 324B6041234D3CA0009FA70D /* Release */ = { 280 | isa = XCBuildConfiguration; 281 | buildSettings = { 282 | ALWAYS_SEARCH_USER_PATHS = NO; 283 | CLANG_ANALYZER_NONNULL = YES; 284 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 285 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 286 | CLANG_CXX_LIBRARY = "libc++"; 287 | CLANG_ENABLE_MODULES = YES; 288 | CLANG_ENABLE_OBJC_ARC = YES; 289 | CLANG_ENABLE_OBJC_WEAK = YES; 290 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 291 | CLANG_WARN_BOOL_CONVERSION = YES; 292 | CLANG_WARN_COMMA = YES; 293 | CLANG_WARN_CONSTANT_CONVERSION = YES; 294 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 295 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 296 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 297 | CLANG_WARN_EMPTY_BODY = YES; 298 | CLANG_WARN_ENUM_CONVERSION = YES; 299 | CLANG_WARN_INFINITE_RECURSION = YES; 300 | CLANG_WARN_INT_CONVERSION = YES; 301 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 302 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 303 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 304 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 305 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 306 | CLANG_WARN_STRICT_PROTOTYPES = YES; 307 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 308 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 309 | CLANG_WARN_UNREACHABLE_CODE = YES; 310 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 311 | COPY_PHASE_STRIP = NO; 312 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 313 | ENABLE_NS_ASSERTIONS = NO; 314 | ENABLE_STRICT_OBJC_MSGSEND = YES; 315 | GCC_C_LANGUAGE_STANDARD = gnu11; 316 | GCC_NO_COMMON_BLOCKS = YES; 317 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 318 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 319 | GCC_WARN_UNDECLARED_SELECTOR = YES; 320 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 321 | GCC_WARN_UNUSED_FUNCTION = YES; 322 | GCC_WARN_UNUSED_VARIABLE = YES; 323 | IPHONEOS_DEPLOYMENT_TARGET = 13.0; 324 | MTL_ENABLE_DEBUG_INFO = NO; 325 | MTL_FAST_MATH = YES; 326 | SDKROOT = iphoneos; 327 | SWIFT_COMPILATION_MODE = wholemodule; 328 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 329 | VALIDATE_PRODUCT = YES; 330 | }; 331 | name = Release; 332 | }; 333 | 324B6043234D3CA0009FA70D /* Debug */ = { 334 | isa = XCBuildConfiguration; 335 | baseConfigurationReference = E6FED3283B3AF78AB183D784 /* Pods-TextToSVG.debug.xcconfig */; 336 | buildSettings = { 337 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 338 | CODE_SIGN_STYLE = Automatic; 339 | DEVELOPMENT_TEAM = HT3GA3Z42X; 340 | INFOPLIST_FILE = TextToSVG/Info.plist; 341 | LD_RUNPATH_SEARCH_PATHS = ( 342 | "$(inherited)", 343 | "@executable_path/Frameworks", 344 | ); 345 | PRODUCT_BUNDLE_IDENTIFIER = cat.thepirate.TextToSVG; 346 | PRODUCT_NAME = "$(TARGET_NAME)"; 347 | SWIFT_VERSION = 5.0; 348 | TARGETED_DEVICE_FAMILY = "1,2"; 349 | }; 350 | name = Debug; 351 | }; 352 | 324B6044234D3CA0009FA70D /* Release */ = { 353 | isa = XCBuildConfiguration; 354 | baseConfigurationReference = 10508FD6454EC7889AD3B05A /* Pods-TextToSVG.release.xcconfig */; 355 | buildSettings = { 356 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 357 | CODE_SIGN_STYLE = Automatic; 358 | DEVELOPMENT_TEAM = HT3GA3Z42X; 359 | INFOPLIST_FILE = TextToSVG/Info.plist; 360 | LD_RUNPATH_SEARCH_PATHS = ( 361 | "$(inherited)", 362 | "@executable_path/Frameworks", 363 | ); 364 | PRODUCT_BUNDLE_IDENTIFIER = cat.thepirate.TextToSVG; 365 | PRODUCT_NAME = "$(TARGET_NAME)"; 366 | SWIFT_VERSION = 5.0; 367 | TARGETED_DEVICE_FAMILY = "1,2"; 368 | }; 369 | name = Release; 370 | }; 371 | /* End XCBuildConfiguration section */ 372 | 373 | /* Begin XCConfigurationList section */ 374 | 324B6029234D3C9F009FA70D /* Build configuration list for PBXProject "TextToSVG" */ = { 375 | isa = XCConfigurationList; 376 | buildConfigurations = ( 377 | 324B6040234D3CA0009FA70D /* Debug */, 378 | 324B6041234D3CA0009FA70D /* Release */, 379 | ); 380 | defaultConfigurationIsVisible = 0; 381 | defaultConfigurationName = Release; 382 | }; 383 | 324B6042234D3CA0009FA70D /* Build configuration list for PBXNativeTarget "TextToSVG" */ = { 384 | isa = XCConfigurationList; 385 | buildConfigurations = ( 386 | 324B6043234D3CA0009FA70D /* Debug */, 387 | 324B6044234D3CA0009FA70D /* Release */, 388 | ); 389 | defaultConfigurationIsVisible = 0; 390 | defaultConfigurationName = Release; 391 | }; 392 | /* End XCConfigurationList section */ 393 | }; 394 | rootObject = 324B6026234D3C9F009FA70D /* Project object */; 395 | } 396 | -------------------------------------------------------------------------------- /Samples/TextToSVG/TextToSVG.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Samples/TextToSVG/TextToSVG.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Samples/TextToSVG/TextToSVG/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // TextToSVG 4 | // 5 | // Created by Mateusz Malczak on 08/10/2019. 6 | // 7 | 8 | import UIKit 9 | 10 | @UIApplicationMain 11 | class AppDelegate: UIResponder, UIApplicationDelegate { 12 | 13 | 14 | 15 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 16 | // Override point for customization after application launch. 17 | return true 18 | } 19 | 20 | // MARK: UISceneSession Lifecycle 21 | 22 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { 23 | // Called when a new scene session is being created. 24 | // Use this method to select a configuration to create the new scene with. 25 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) 26 | } 27 | 28 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { 29 | // Called when the user discards a scene session. 30 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. 31 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return. 32 | } 33 | 34 | 35 | } 36 | 37 | -------------------------------------------------------------------------------- /Samples/TextToSVG/TextToSVG/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /Samples/TextToSVG/TextToSVG/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Samples/TextToSVG/TextToSVG/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Samples/TextToSVG/TextToSVG/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum. 46 | 47 | 48 | 49 | 50 | 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 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | -------------------------------------------------------------------------------- /Samples/TextToSVG/TextToSVG/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UIApplicationSceneManifest 24 | 25 | UIApplicationSupportsMultipleScenes 26 | 27 | UISceneConfigurations 28 | 29 | UIWindowSceneSessionRoleApplication 30 | 31 | 32 | UISceneConfigurationName 33 | Default Configuration 34 | UISceneDelegateClassName 35 | $(PRODUCT_MODULE_NAME).SceneDelegate 36 | UISceneStoryboardFile 37 | Main 38 | 39 | 40 | 41 | 42 | UILaunchStoryboardName 43 | LaunchScreen 44 | UIMainStoryboardFile 45 | Main 46 | UIRequiredDeviceCapabilities 47 | 48 | armv7 49 | 50 | UISupportedInterfaceOrientations 51 | 52 | UIInterfaceOrientationPortrait 53 | UIInterfaceOrientationLandscapeLeft 54 | UIInterfaceOrientationLandscapeRight 55 | 56 | UISupportedInterfaceOrientations~ipad 57 | 58 | UIInterfaceOrientationPortrait 59 | UIInterfaceOrientationPortraitUpsideDown 60 | UIInterfaceOrientationLandscapeLeft 61 | UIInterfaceOrientationLandscapeRight 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /Samples/TextToSVG/TextToSVG/SceneDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.swift 3 | // TextToSVG 4 | // 5 | // Created by Mateusz Malczak on 08/10/2019. 6 | // 7 | 8 | import UIKit 9 | 10 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { 11 | 12 | var window: UIWindow? 13 | 14 | 15 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 16 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. 17 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. 18 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). 19 | guard let _ = (scene as? UIWindowScene) else { return } 20 | } 21 | 22 | func sceneDidDisconnect(_ scene: UIScene) { 23 | // Called as the scene is being released by the system. 24 | // This occurs shortly after the scene enters the background, or when its session is discarded. 25 | // Release any resources associated with this scene that can be re-created the next time the scene connects. 26 | // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead). 27 | } 28 | 29 | func sceneDidBecomeActive(_ scene: UIScene) { 30 | // Called when the scene has moved from an inactive state to an active state. 31 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. 32 | } 33 | 34 | func sceneWillResignActive(_ scene: UIScene) { 35 | // Called when the scene will move from an active state to an inactive state. 36 | // This may occur due to temporary interruptions (ex. an incoming phone call). 37 | } 38 | 39 | func sceneWillEnterForeground(_ scene: UIScene) { 40 | // Called as the scene transitions from the background to the foreground. 41 | // Use this method to undo the changes made on entering the background. 42 | } 43 | 44 | func sceneDidEnterBackground(_ scene: UIScene) { 45 | // Called as the scene transitions from the foreground to the background. 46 | // Use this method to save data, release shared resources, and store enough scene-specific state information 47 | // to restore the scene back to its current state. 48 | } 49 | 50 | 51 | } 52 | 53 | -------------------------------------------------------------------------------- /Samples/TextToSVG/TextToSVG/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // TextToSVG 4 | // 5 | // Created by Mateusz Malczak on 08/10/2019. 6 | // 7 | 8 | import UIKit 9 | import WebKit 10 | import TextPaths 11 | 12 | class ViewController: UIViewController { 13 | 14 | @IBOutlet weak var textView: UITextView! 15 | 16 | @IBOutlet weak var svgTextView: UITextView! 17 | 18 | @IBOutlet weak var webView: WKWebView! 19 | 20 | @IBAction func switchChanged(_ control: UISegmentedControl) { 21 | webView.isHidden = control.selectedSegmentIndex == 0 22 | } 23 | 24 | override func viewDidAppear(_ animated: Bool) { 25 | super.viewDidAppear(animated) 26 | getSVG() 27 | } 28 | 29 | func getSVG() { 30 | let hardcodedTextViewMargins: CGFloat = 12 31 | 32 | if let attributedText = textView.attributedText { 33 | let bounds = CGSize(width: textView.frame.width - hardcodedTextViewMargins, 34 | height: CGFloat.greatestFiniteMagnitude) 35 | let textPath = attributedText.getTextPath(InBounds: bounds, 36 | withAttributes: false, 37 | withPath: true) 38 | if let composedPath = textPath?.composedPath, let composedBounds = textPath?.composedBounds { 39 | 40 | let svg_path = svg(fromPath: composedPath) 41 | 42 | let svg = """ 43 | 44 | 45 | 46 | 47 | """ 48 | 49 | svgTextView.text = svg 50 | 51 | let html = """ 52 | 53 | 54 | TextPaths 55 | 56 | 57 | 58 |

SVG Preview

Text below is a SVG path

59 |
\(svg)
60 | 61 | 62 | """ 63 | 64 | webView.loadHTMLString(html, baseURL: nil) 65 | } 66 | } 67 | } 68 | 69 | func svg(fromPath path: CGPath) -> String { 70 | var data = Data() 71 | path.apply(info: &data) { userData, elementPtr in 72 | var data = userData!.assumingMemoryBound(to: Data.self).pointee 73 | let element = elementPtr.pointee 74 | switch element.type { 75 | case .moveToPoint: 76 | let point = element.points.pointee 77 | data.append(String(format: "M%.2f,%.2f", point.x, point.y).data(using: .utf8)!) 78 | break; 79 | case .addLineToPoint: 80 | let point = element.points.pointee 81 | data.append(String(format: "L%.2f,%.2f", point.x, point.y).data(using: .utf8)!) 82 | break; 83 | case .addQuadCurveToPoint: 84 | let ctrl = element.points.pointee 85 | let point = element.points.advanced(by: 1).pointee 86 | 87 | data.append(String(format: "Q%.2f,%.2f,%.2f,%.2f", ctrl.x, ctrl.y, point.x, point.y).data(using: .utf8)!) 88 | break 89 | case .addCurveToPoint: 90 | let ctrl1 = element.points.pointee 91 | let ctrl2 = element.points.advanced(by: 1).pointee 92 | let point = element.points.advanced(by: 2).pointee 93 | data.append(String(format: "C%.2f,%.2f,%.2f,%.2f,%.2f,%.2f", ctrl1.x, ctrl1.y, ctrl2.x, ctrl2.y, point.x, point.y).data(using: .utf8)!) 94 | break 95 | case .closeSubpath: 96 | data.append("Z".data(using: .utf8)!) 97 | break 98 | @unknown default: 99 | break 100 | } 101 | userData!.assumingMemoryBound(to: Data.self).pointee = data 102 | } 103 | 104 | return String(bytes: data, encoding: .utf8)! 105 | } 106 | 107 | } 108 | 109 | extension ViewController: UITextViewDelegate { 110 | func textViewDidChange(_ textView: UITextView) { 111 | getSVG() 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /Source/NSAttributedString.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSAttributedString.swift 3 | // TextPaths 4 | // 5 | // Created by Mateusz Malczak on 21/01/17. 6 | // Copyright © 2017 The Pirate Cat. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CoreGraphics 11 | 12 | typealias TextPathAttributes = [NSAttributedString.Key:Any] 13 | 14 | /** 15 | Class represents single character representation - glyph 16 | */ 17 | public class TextPathGlyph { 18 | public typealias Index = String.UnicodeScalarView.Index 19 | 20 | /// Character index in source string unicode view 21 | public fileprivate(set) var index: Index 22 | 23 | /// Glyph path defined in glyph coordinates 24 | public fileprivate(set) var path: CGPath 25 | 26 | /// Glyph path position (top left corner) in line space 27 | public fileprivate(set) var position: CGPoint 28 | 29 | /// Glyph line advance 30 | public fileprivate(set) var advance = CGSize.zero 31 | 32 | /// Glyph origin offset (x component is a glyph origin offset, y component is an offset to baseline) 33 | public fileprivate(set) var originOffset = CGPoint.zero 34 | 35 | /// Glyph line run index 36 | fileprivate var lineRun: Int = 0 37 | 38 | /// Glyph line 39 | fileprivate weak var line: TextPathLine? 40 | 41 | /// Get glyph attributes as defined in source string 42 | public var attributes: [NSAttributedString.Key:Any]? { 43 | return line?.attributes(ForGlyph: self) 44 | } 45 | 46 | init(index: Index, path: CGPath, position: CGPoint){ 47 | self.index = index 48 | self.path = path 49 | self.position = position 50 | } 51 | } 52 | 53 | /** 54 | Single text line representation 55 | */ 56 | public class TextPathLine { 57 | 58 | /// Line index 59 | public fileprivate(set) var index: Int 60 | 61 | /** 62 | Line typographic bounds based on line ascent / descent 63 | 64 | Rectangle is based on typographic line properties (ie. ascent, descent) 65 | */ 66 | public fileprivate(set) var lineBounds = CGRect.zero 67 | 68 | /** 69 | Line path bounds based on text path 70 | 71 | Rectangle defined by _textBounds_ is smaller than _lineBounds_ and is based only o text bounds 72 | */ 73 | public fileprivate(set) var textBounds = CGRect.zero 74 | 75 | /// Line leading 76 | public fileprivate(set) var leading = CGFloat(0.0) 77 | 78 | /// Line ascent 79 | public fileprivate(set) var ascent = CGFloat(0.0) 80 | 81 | /// Line descent 82 | public fileprivate(set) var descent = CGFloat(0.0) 83 | 84 | /** 85 | Line effective descent is calculated based on lineRuns typographic properties, 86 | in most cases is equal to _descent_. In some rare cases this rect is smaller that _descent_ property. 87 | 88 | ## Example ## 89 | ```` 90 | let str = NSMutableAttributedString(string: "First line".uppercased(), attributes: [ 91 | NSFontAttributeName: UIFont.systemFont(ofSize: 22, weight: UIFontWeightUltraLight) 92 | ]) 93 | str.append(NSAttributedString(string: "\nSecond line".uppercased(), attributes: [ 94 | NSFontAttributeName: UIFont.systemFont(ofSize: 64, weight: UIFontWeightMedium) 95 | ])) 96 | ```` 97 | In second line much larger font is used, it also applied to the 'line break' (\n) 98 | character. As a result typographic size of the first line is different that one used by TextKit to draw text 99 | in text components (UILabel, UITextView). 100 | In this case _effectiveDescent_ of the first line is smaller that _descent_ measured by CoreText, because it is calculated 101 | only based on visible characters (ie. not including '\n' metrics) 102 | */ 103 | public fileprivate(set) var effectiveDescent = CGFloat(0.0) 104 | 105 | /// Line effective ascent (read more above) 106 | public fileprivate(set) var effectiveAscent = CGFloat(0.0) 107 | 108 | /// Line glyph to attributes mapping 109 | fileprivate var attributes: [TextPathAttributes]? 110 | 111 | /// Collection of all glyphs in line 112 | fileprivate var glyphs = [TextPathGlyph]() 113 | 114 | init(index: Int) { 115 | self.index = index 116 | } 117 | 118 | /** 119 | Enumerates over all glyphs in line 120 | - Parameter callback: Called for each glyph in line 121 | - Parameter line: Current text line 122 | - Parameter glyph: Current glyph 123 | */ 124 | public func enumerateGlyphs(_ callback:(_ line: TextPathLine, _ glyph: TextPathGlyph) -> ()) { 125 | for glyph in glyphs { 126 | callback(self, glyph) 127 | } 128 | } 129 | 130 | /** 131 | Get attributes for a line glyph 132 | - Parameter glyph: Glyph in line 133 | - Returns: Glyph attributes 134 | */ 135 | fileprivate func attributes(ForGlyph glyph: TextPathGlyph) -> TextPathAttributes? { 136 | if let attributes = attributes { 137 | return attributes[glyph.lineRun] 138 | } 139 | return nil 140 | } 141 | } 142 | 143 | /** 144 | Text frame representation. This can represent a simple text rectangle (eq. UITextView text content), 145 | as well as a complex frame defined by CGPath 146 | */ 147 | public class TextPathFrame { 148 | 149 | /// Frame shape path (eq. textfield bounds rect) 150 | public fileprivate(set) var path: CGPath 151 | 152 | /// Text frame lines 153 | public fileprivate(set) var lines = [TextPathLine]() 154 | 155 | init(path: CGPath) { 156 | self.path = path 157 | } 158 | 159 | /** 160 | Enumerates over all glyphs in text frame 161 | - Parameter callback: Called for each glyph in text frame 162 | - Parameter line: Current text line 163 | - Parameter glyph: Current glyph 164 | */ 165 | public func enumerateGlyphs(_ callback: @escaping(_ line: TextPathLine, _ glyph: TextPathGlyph) -> ()) { 166 | for line in lines { 167 | line.enumerateGlyphs(callback) 168 | } 169 | } 170 | } 171 | 172 | /** 173 | Text path represents a CGPath representation of text frame, glyphs and typographic properties 174 | */ 175 | public class TextPath { 176 | 177 | /// Attributed text used to generate text path 178 | public fileprivate(set) var attributedString: NSAttributedString 179 | 180 | /// Text path composed for input text 181 | public fileprivate(set) var composedPath: CGPath? 182 | 183 | /// Composed text path bounding box 184 | public fileprivate(set) var composedBounds: CGRect 185 | 186 | /// text frames 187 | public fileprivate(set) var frames = [TextPathFrame]() 188 | 189 | init(text: NSAttributedString, path: CGPath? = nil) { 190 | self.attributedString = text 191 | self.composedPath = path 192 | self.composedBounds = path?.boundingBoxOfPath ?? CGRect.zero 193 | } 194 | } 195 | 196 | public extension NSAttributedString { 197 | 198 | /** 199 | Creates a text path and a collection of text lines and 200 | glyphs with additional typographic informations (ie. ascent, descent, bounds) 201 | 202 | - Parameter bounds: text bounding box 203 | - Parameter withAttributes: if _true_ glyph attributes are included in returned TextPath 204 | - Parameter withPath: if _true_ a composed text path is included 205 | - Returns: created text path or NULL if failed 206 | */ 207 | func getTextPath(InBounds bounds:CGSize, withAttributes: Bool = false, withPath: Bool = true) -> TextPath? { 208 | let clearText = self.string 209 | if clearText.isEmpty { 210 | return nil 211 | } 212 | 213 | let fontAttributeKey = NSAttributedString.Key.font 214 | let defaultAttributes: TextPathAttributes = [ 215 | fontAttributeKey: UIFont.systemFont(ofSize: UIFont.systemFontSize), 216 | NSAttributedString.Key.foregroundColor: UIColor.black 217 | ] 218 | 219 | var lineIndex = 0 220 | let unicodeScalars = clearText.unicodeScalars 221 | var unicodeIndex = unicodeScalars.startIndex 222 | 223 | let frameSetter = CTFramesetterCreateWithAttributedString(self) 224 | let textRange = CFRangeMake(0, self.length) 225 | let frameSize = CTFramesetterSuggestFrameSizeWithConstraints(frameSetter, textRange, nil, bounds, nil) 226 | let framePath = UIBezierPath(rect: CGRect(origin: .zero, size: frameSize)).cgPath 227 | let frame = CTFramesetterCreateFrame(frameSetter, textRange, framePath, nil) 228 | 229 | let tpFrame = TextPathFrame(path: framePath) 230 | let frames = [tpFrame] 231 | 232 | // 0. fetch all lines / glyphs + text path 233 | let ignoredCharsSet = CharacterSet.whitespacesAndNewlines 234 | let path = CGMutablePath() 235 | 236 | if let lines = CTFrameGetLines(frame) as? [CTLine] { 237 | var linesShift = CGFloat(0) 238 | var origins = [CGPoint](repeating: CGPoint.zero, count: lines.count) 239 | CTFrameGetLineOrigins(frame, CFRangeMake(0, lines.count), &origins) 240 | var originItr = origins.makeIterator() 241 | 242 | for line in lines { 243 | let lineOrigin = originItr.next() ?? CGPoint.zero 244 | let tpLine = TextPathLine(index: lineIndex) 245 | 246 | tpLine.lineBounds = CTLineGetBoundsWithOptions(line, .excludeTypographicLeading) 247 | tpLine.textBounds = CTLineGetBoundsWithOptions(line, .useGlyphPathBounds) 248 | 249 | let _ = CTLineGetTypographicBounds(line, &tpLine.ascent, &tpLine.descent, &tpLine.leading) 250 | 251 | if let lineRuns = CTLineGetGlyphRuns(line) as? [CTRun] { 252 | if withAttributes { 253 | tpLine.attributes = [TextPathAttributes](repeating:defaultAttributes, 254 | count: lineRuns.count) 255 | } 256 | 257 | var effectiveDescent = CGFloat(0) 258 | var effectiveAscent = CGFloat(0) 259 | 260 | var lineRunIndex = 0 261 | for lineRun in lineRuns { 262 | let glyphsCount = CTRunGetGlyphCount(lineRun) 263 | if glyphsCount == 0 { 264 | continue 265 | } 266 | 267 | let attributes = (CTRunGetAttributes(lineRun) as? TextPathAttributes) ?? defaultAttributes 268 | let font = (attributes[fontAttributeKey] as? UIFont) ?? (defaultAttributes[fontAttributeKey] as! UIFont) 269 | 270 | if withAttributes { 271 | tpLine.attributes![lineRunIndex] = attributes 272 | } 273 | 274 | var rt_ascent = CGFloat(0.0) 275 | var rt_descent = CGFloat(0.0) 276 | var rt_leading = CGFloat(0.0) 277 | let _ = CTRunGetTypographicBounds(lineRun, CFRangeMake(0, glyphsCount), &rt_ascent, &rt_descent, &rt_leading) 278 | 279 | let lineRunInfo = (CTRunGetGlyphsPtr(lineRun), CTRunGetPositionsPtr(lineRun), CTRunGetAdvancesPtr(lineRun)) 280 | switch( lineRunInfo ) { 281 | case let (glyphsPtr?, positionsPtr?, advancesPtr?): 282 | var glyphPtr = glyphsPtr 283 | var positionPtr = positionsPtr 284 | var advancePtr = advancesPtr 285 | 286 | for _ in 0.. "mateusz@malczak.info" } 9 | s.source = { :git => "https://github.com/malczak/TextPaths.git", :branch => "swift" } 10 | 11 | s.platform = :ios, "8.0" 12 | 13 | s.source_files = "Source/*.swift" 14 | s.exclude_files = "Source/*Tests.swift" 15 | 16 | s.requires_arc = true 17 | end 18 | -------------------------------------------------------------------------------- /_Assets/textpaths-bounds-01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/malczak/TextPaths/f1c9ded08d86e6ce44446a370121893a3b95b4f2/_Assets/textpaths-bounds-01.jpg -------------------------------------------------------------------------------- /_Assets/textpaths-bounds-02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/malczak/TextPaths/f1c9ded08d86e6ce44446a370121893a3b95b4f2/_Assets/textpaths-bounds-02.jpg -------------------------------------------------------------------------------- /_Assets/textpaths-bounds-03.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/malczak/TextPaths/f1c9ded08d86e6ce44446a370121893a3b95b4f2/_Assets/textpaths-bounds-03.jpg -------------------------------------------------------------------------------- /_Assets/textpaths-bounds-04.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/malczak/TextPaths/f1c9ded08d86e6ce44446a370121893a3b95b4f2/_Assets/textpaths-bounds-04.jpg -------------------------------------------------------------------------------- /_Assets/textpaths-falling-uilabel-animation.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/malczak/TextPaths/f1c9ded08d86e6ce44446a370121893a3b95b4f2/_Assets/textpaths-falling-uilabel-animation.gif -------------------------------------------------------------------------------- /_Assets/textpaths-fly-in-text-animation.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/malczak/TextPaths/f1c9ded08d86e6ce44446a370121893a3b95b4f2/_Assets/textpaths-fly-in-text-animation.gif -------------------------------------------------------------------------------- /_Assets/textpaths-nsattributedtext-decompose.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/malczak/TextPaths/f1c9ded08d86e6ce44446a370121893a3b95b4f2/_Assets/textpaths-nsattributedtext-decompose.jpg -------------------------------------------------------------------------------- /_Assets/textpaths-nsattributedtext-to-svg-01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/malczak/TextPaths/f1c9ded08d86e6ce44446a370121893a3b95b4f2/_Assets/textpaths-nsattributedtext-to-svg-01.jpg -------------------------------------------------------------------------------- /_Assets/textpaths-nsattributedtext-to-svg-02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/malczak/TextPaths/f1c9ded08d86e6ce44446a370121893a3b95b4f2/_Assets/textpaths-nsattributedtext-to-svg-02.jpg --------------------------------------------------------------------------------