├── Sample ├── Pods │ ├── Pods-TTCounterLabel.xcconfig │ ├── Headers │ │ └── TTTAttributedLabel │ │ │ └── TTTAttributedLabel.h │ ├── BuildHeaders │ │ └── TTTAttributedLabel │ │ │ └── TTTAttributedLabel.h │ ├── Pods-TTTAttributedLabel.xcconfig │ ├── Pods-TTCounterLabel-prefix.pch │ ├── Pods-TTTAttributedLabel-prefix.pch │ ├── Pods-dummy.m │ ├── Pods-TTCounterLabel-dummy.m │ ├── Pods-TTTAttributedLabel-dummy.m │ ├── Manifest.lock │ ├── Pods-TTCounterLabel-Private.xcconfig │ ├── Pods.xcconfig │ ├── Pods-TTTAttributedLabel-Private.xcconfig │ ├── Pods-environment.h │ ├── TTTAttributedLabel │ │ ├── LICENSE │ │ ├── README.md │ │ └── TTTAttributedLabel │ │ │ ├── TTTAttributedLabel.h │ │ │ └── TTTAttributedLabel.m │ ├── Pods-acknowledgements.markdown │ ├── Pods-acknowledgements.plist │ ├── Pods-resources.sh │ └── Pods.xcodeproj │ │ └── project.pbxproj ├── Podfile ├── Sample │ ├── en.lproj │ │ └── InfoPlist.strings │ ├── ViewController.h │ ├── AppDelegate.h │ ├── main.m │ ├── Sample-Prefix.pch │ ├── Images.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── LaunchImage.launchimage │ │ │ └── Contents.json │ ├── Sample-Info.plist │ ├── AppDelegate.m │ ├── ViewController.m │ └── Base.lproj │ │ └── Main.storyboard ├── SampleTests │ ├── en.lproj │ │ └── InfoPlist.strings │ ├── SampleTests.m │ └── SampleTests-Info.plist ├── Sample.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── Sample.xccheckout ├── Podfile.lock └── Sample.xcodeproj │ ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── Sample.xccheckout │ └── project.pbxproj ├── screenshot.PNG ├── .gitignore ├── LICENSE ├── TTCounterLabel.podspec ├── Source ├── TTCounterLabel.h └── TTCounterLabel.m └── README.md /Sample/Pods/Pods-TTCounterLabel.xcconfig: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Sample/Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '6.0' 2 | 3 | pod 'TTTAttributedLabel' -------------------------------------------------------------------------------- /Sample/Sample/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /screenshot.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Triggertrap/TTCounterLabel/HEAD/screenshot.PNG -------------------------------------------------------------------------------- /Sample/SampleTests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Sample/Pods/Headers/TTTAttributedLabel/TTTAttributedLabel.h: -------------------------------------------------------------------------------- 1 | ../../TTTAttributedLabel/TTTAttributedLabel/TTTAttributedLabel.h -------------------------------------------------------------------------------- /Sample/Pods/BuildHeaders/TTTAttributedLabel/TTTAttributedLabel.h: -------------------------------------------------------------------------------- 1 | ../../TTTAttributedLabel/TTTAttributedLabel/TTTAttributedLabel.h -------------------------------------------------------------------------------- /Sample/Pods/Pods-TTTAttributedLabel.xcconfig: -------------------------------------------------------------------------------- 1 | PODS_TTTATTRIBUTEDLABEL_OTHER_LDFLAGS = -framework CoreGraphics -framework CoreText -------------------------------------------------------------------------------- /Sample/Pods/Pods-TTCounterLabel-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #endif 4 | 5 | #import "Pods-environment.h" 6 | -------------------------------------------------------------------------------- /Sample/Pods/Pods-TTTAttributedLabel-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #endif 4 | 5 | #import "Pods-environment.h" 6 | -------------------------------------------------------------------------------- /Sample/Pods/Pods-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods : NSObject 3 | @end 4 | @implementation PodsDummy_Pods 5 | @end 6 | -------------------------------------------------------------------------------- /Sample/Pods/Pods-TTCounterLabel-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_TTCounterLabel : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_TTCounterLabel 5 | @end 6 | -------------------------------------------------------------------------------- /Sample/Pods/Pods-TTTAttributedLabel-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_TTTAttributedLabel : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_TTTAttributedLabel 5 | @end 6 | -------------------------------------------------------------------------------- /Sample/Sample.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Sample/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - TTTAttributedLabel (1.9.4) 3 | 4 | DEPENDENCIES: 5 | - TTTAttributedLabel 6 | 7 | SPEC CHECKSUMS: 8 | TTTAttributedLabel: 617e14e3590a4afc9bc35dd999eb37e888996f36 9 | 10 | COCOAPODS: 0.31.1 11 | -------------------------------------------------------------------------------- /Sample/Sample.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Sample/Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - TTTAttributedLabel (1.9.4) 3 | 4 | DEPENDENCIES: 5 | - TTTAttributedLabel 6 | 7 | SPEC CHECKSUMS: 8 | TTTAttributedLabel: 617e14e3590a4afc9bc35dd999eb37e888996f36 9 | 10 | COCOAPODS: 0.31.1 11 | -------------------------------------------------------------------------------- /Sample/Sample/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // Sample 4 | // 5 | // Created by Ross Gibson on 15/10/2013. 6 | // Copyright (c) 2013 Triggertrap. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | .DS_Store 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | profile 14 | *.moved-aside 15 | DerivedData 16 | .idea/ 17 | .hmap 18 | .xccheckout 19 | -------------------------------------------------------------------------------- /Sample/Sample/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // Sample 4 | // 5 | // Created by Ross Gibson on 15/10/2013. 6 | // Copyright (c) 2013 Triggertrap. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Sample/Pods/Pods-TTCounterLabel-Private.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods-TTCounterLabel.xcconfig" 2 | GCC_PREPROCESSOR_DEFINITIONS = COCOAPODS=1 3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/BuildHeaders" "${PODS_ROOT}/BuildHeaders/TTCounterLabel" "${PODS_ROOT}/Headers" "${PODS_ROOT}/Headers/TTCounterLabel" "${PODS_ROOT}/Headers/TTTAttributedLabel" 4 | OTHER_LDFLAGS = -ObjC 5 | PODS_ROOT = ${SRCROOT} -------------------------------------------------------------------------------- /Sample/Pods/Pods.xcconfig: -------------------------------------------------------------------------------- 1 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 2 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers" "${PODS_ROOT}/Headers/TTTAttributedLabel" 3 | OTHER_CFLAGS = $(inherited) -isystem "${PODS_ROOT}/Headers" -isystem "${PODS_ROOT}/Headers/TTTAttributedLabel" 4 | OTHER_LDFLAGS = -ObjC -framework CoreGraphics -framework CoreText 5 | PODS_ROOT = ${SRCROOT}/Pods -------------------------------------------------------------------------------- /Sample/Pods/Pods-TTTAttributedLabel-Private.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods-TTTAttributedLabel.xcconfig" 2 | GCC_PREPROCESSOR_DEFINITIONS = COCOAPODS=1 3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/BuildHeaders" "${PODS_ROOT}/BuildHeaders/TTTAttributedLabel" "${PODS_ROOT}/Headers" "${PODS_ROOT}/Headers/TTTAttributedLabel" 4 | OTHER_LDFLAGS = -ObjC ${PODS_TTTATTRIBUTEDLABEL_OTHER_LDFLAGS} 5 | PODS_ROOT = ${SRCROOT} -------------------------------------------------------------------------------- /Sample/Sample/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // Sample 4 | // 5 | // Created by Ross Gibson on 15/10/2013. 6 | // Copyright (c) 2013 Triggertrap. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "AppDelegate.h" 12 | 13 | int main(int argc, char * argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Sample/Sample/Sample-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #import 8 | 9 | #ifndef __IPHONE_5_0 10 | #warning "This project uses features only available in iOS SDK 5.0 and later." 11 | #endif 12 | 13 | #ifdef __OBJC__ 14 | #import 15 | #import 16 | #endif 17 | -------------------------------------------------------------------------------- /Sample/Sample/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "40x40", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "60x60", 16 | "scale" : "2x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Sample/Pods/Pods-environment.h: -------------------------------------------------------------------------------- 1 | 2 | // To check if a library is compiled with CocoaPods you 3 | // can use the `COCOAPODS` macro definition which is 4 | // defined in the xcconfigs so it is available in 5 | // headers also when they are imported in the client 6 | // project. 7 | 8 | 9 | // TTTAttributedLabel 10 | #define COCOAPODS_POD_AVAILABLE_TTTAttributedLabel 11 | #define COCOAPODS_VERSION_MAJOR_TTTAttributedLabel 1 12 | #define COCOAPODS_VERSION_MINOR_TTTAttributedLabel 9 13 | #define COCOAPODS_VERSION_PATCH_TTTAttributedLabel 4 14 | 15 | -------------------------------------------------------------------------------- /Sample/Sample/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "7.0", 8 | "scale" : "2x" 9 | }, 10 | { 11 | "orientation" : "portrait", 12 | "idiom" : "iphone", 13 | "subtype" : "retina4", 14 | "extent" : "full-screen", 15 | "minimum-system-version" : "7.0", 16 | "scale" : "2x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Sample/SampleTests/SampleTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // SampleTests.m 3 | // SampleTests 4 | // 5 | // Created by Ross Gibson on 15/10/2013. 6 | // Copyright (c) 2013 Triggertrap. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SampleTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation SampleTests 16 | 17 | - (void)setUp 18 | { 19 | [super setUp]; 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | } 22 | 23 | - (void)tearDown 24 | { 25 | // Put teardown code here. This method is called after the invocation of each test method in the class. 26 | [super tearDown]; 27 | } 28 | 29 | - (void)testExample 30 | { 31 | XCTFail(@"No implementation for \"%s\"", __PRETTY_FUNCTION__); 32 | } 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /Sample/SampleTests/SampleTests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | com.triggertrap.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Sample/Pods/TTTAttributedLabel/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 Mattt Thompson (http://mattt.me/) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Triggertrap Ltd & Copyright (c) 2013 Ross Gibson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | 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, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /TTCounterLabel.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "TTCounterLabel" 3 | s.version = "1.2.0" 4 | s.summary = "A custom UILabel that acts a time counter, counting up or down and formatting the string to hours, minutes, seconds and milliseconds." 5 | s.description = "This CocoaPod is designed to accept a value in milliseconds that is then displayed it in a time friendly format. Currently the controls supports up-to a maximum value of 99 hours 59 minutes 59 seconds and 999 milliseconds, which should be enough for most uses. The control automatically removes any leading zeros and centralises the result. It also supports different fonts for each unit division." 6 | s.homepage = "https://github.com/TriggerTrap/TTCounterLabel" 7 | s.license = { :type => 'MIT', :file => 'LICENSE' } 8 | s.authors = { "Triggertrap Ltd" => "support@triggertrap.com", "Ross Gibson" => "ross@triggertrap.com" } 9 | s.platform = :ios, '6.0' 10 | s.source = { :git => "https://github.com/TriggerTrap/TTCounterLabel.git", :tag => "1.2.0" } 11 | s.source_files = 'Source', 'Source/**/*.{h,m}' 12 | s.requires_arc = true 13 | s.dependency 'TTTAttributedLabel' 14 | end -------------------------------------------------------------------------------- /Sample/Sample/Sample-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | TTCounterLabel 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | com.triggertrap.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.2.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 20140724 25 | LSRequiresIPhoneOS 26 | 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /Sample/Pods/Pods-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## TTTAttributedLabel 5 | 6 | Copyright (c) 2011 Mattt Thompson (http://mattt.me/) 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | 26 | Generated by CocoaPods - http://cocoapods.org 27 | -------------------------------------------------------------------------------- /Source/TTCounterLabel.h: -------------------------------------------------------------------------------- 1 | // 2 | // TTCounterLabel.h 3 | // TTCounterLabel 4 | // 5 | // Created by Ross Gibson on 10/10/2013. 6 | // Copyright (c) 2013 Triggertrap. All rights reserved. 7 | // 8 | 9 | #import "TTTAttributedLabel.h" 10 | 11 | typedef NS_ENUM(NSInteger, kCountDirection){ 12 | kCountDirectionUp = 0, 13 | kCountDirectionDown 14 | }; 15 | 16 | typedef NS_ENUM(NSInteger, kDisplayMode) { 17 | kDisplayModeFull = 0, 18 | kDisplayModeSeconds = 1 19 | }; 20 | 21 | #pragma mark - TTCounterLabelDelegate 22 | 23 | @class TTCounterLabel; 24 | 25 | @protocol TTCounterLabelDelegate 26 | @optional 27 | - (void)countdownDidEndForSource:(TTCounterLabel *)source; 28 | @end 29 | 30 | #pragma mark - TTCounterLabel 31 | 32 | @interface TTCounterLabel : TTTAttributedLabel 33 | 34 | @property (weak) id countdownDelegate; 35 | @property (assign, nonatomic) unsigned long long currentValue; 36 | @property (assign, nonatomic) unsigned long long startValue; 37 | @property (assign, nonatomic) NSInteger countDirection; 38 | @property (strong, nonatomic) UIFont *boldFont; 39 | @property (strong, nonatomic) UIFont *regularFont; 40 | @property (assign, nonatomic) BOOL isRunning; 41 | @property (assign, nonatomic) kDisplayMode displayMode; 42 | 43 | #pragma mark - Public 44 | 45 | - (void)start; 46 | - (void)stop; 47 | - (void)reset; 48 | - (void)updateApperance; 49 | 50 | @end 51 | -------------------------------------------------------------------------------- /Sample/Sample.xcworkspace/xcshareddata/Sample.xccheckout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDESourceControlProjectFavoriteDictionaryKey 6 | 7 | IDESourceControlProjectIdentifier 8 | 15CE0E8D-3918-4CB7-888A-E3138EFA2EF9 9 | IDESourceControlProjectName 10 | Sample 11 | IDESourceControlProjectOriginsDictionary 12 | 13 | 509D41DF-743E-444D-B109-28A68AE08723 14 | ssh://github.com/TriggerTrap/TTCounterLabel.git 15 | 16 | IDESourceControlProjectPath 17 | Sample/Sample.xcworkspace 18 | IDESourceControlProjectRelativeInstallPathDictionary 19 | 20 | 509D41DF-743E-444D-B109-28A68AE08723 21 | ../.. 22 | 23 | IDESourceControlProjectURL 24 | ssh://github.com/TriggerTrap/TTCounterLabel.git 25 | IDESourceControlProjectVersion 26 | 110 27 | IDESourceControlProjectWCCIdentifier 28 | 509D41DF-743E-444D-B109-28A68AE08723 29 | IDESourceControlProjectWCConfigurations 30 | 31 | 32 | IDESourceControlRepositoryExtensionIdentifierKey 33 | public.vcs.git 34 | IDESourceControlWCCIdentifierKey 35 | 509D41DF-743E-444D-B109-28A68AE08723 36 | IDESourceControlWCCName 37 | TTCounterLabel 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /Sample/Sample.xcodeproj/project.xcworkspace/xcshareddata/Sample.xccheckout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDESourceControlProjectFavoriteDictionaryKey 6 | 7 | IDESourceControlProjectIdentifier 8 | 3723A8CA-50D5-47E3-B32F-D6F6F09E635C 9 | IDESourceControlProjectName 10 | Sample 11 | IDESourceControlProjectOriginsDictionary 12 | 13 | 61CC4AEC-81B1-4216-9A9F-3BAD9D4D2C6A 14 | https://github.com/Ross-Gibson/TTCounterLabel.git 15 | 16 | IDESourceControlProjectPath 17 | Sample/Sample.xcodeproj/project.xcworkspace 18 | IDESourceControlProjectRelativeInstallPathDictionary 19 | 20 | 61CC4AEC-81B1-4216-9A9F-3BAD9D4D2C6A 21 | ../../.. 22 | 23 | IDESourceControlProjectURL 24 | https://github.com/Ross-Gibson/TTCounterLabel.git 25 | IDESourceControlProjectVersion 26 | 110 27 | IDESourceControlProjectWCCIdentifier 28 | 61CC4AEC-81B1-4216-9A9F-3BAD9D4D2C6A 29 | IDESourceControlProjectWCConfigurations 30 | 31 | 32 | IDESourceControlRepositoryExtensionIdentifierKey 33 | public.vcs.git 34 | IDESourceControlWCCIdentifierKey 35 | 61CC4AEC-81B1-4216-9A9F-3BAD9D4D2C6A 36 | IDESourceControlWCCName 37 | TTCounterLabel 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /Sample/Sample/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // Sample 4 | // 5 | // Created by Ross Gibson on 15/10/2013. 6 | // Copyright (c) 2013 Triggertrap. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @implementation AppDelegate 12 | 13 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 14 | // Override point for customization after application launch. 15 | return YES; 16 | } 17 | 18 | - (void)applicationWillResignActive:(UIApplication *)application { 19 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 20 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 21 | } 22 | 23 | - (void)applicationDidEnterBackground:(UIApplication *)application { 24 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 25 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 26 | } 27 | 28 | - (void)applicationWillEnterForeground:(UIApplication *)application { 29 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 30 | } 31 | 32 | - (void)applicationDidBecomeActive:(UIApplication *)application { 33 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 34 | } 35 | 36 | - (void)applicationWillTerminate:(UIApplication *)application { 37 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /Sample/Pods/Pods-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | Copyright (c) 2011 Mattt Thompson (http://mattt.me/) 18 | 19 | Permission is hereby granted, free of charge, to any person obtaining a copy 20 | of this software and associated documentation files (the "Software"), to deal 21 | in the Software without restriction, including without limitation the rights 22 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 23 | copies of the Software, and to permit persons to whom the Software is 24 | furnished to do so, subject to the following conditions: 25 | 26 | The above copyright notice and this permission notice shall be included in 27 | all copies or substantial portions of the Software. 28 | 29 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 30 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 31 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 32 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 33 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 34 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 35 | THE SOFTWARE. 36 | 37 | Title 38 | TTTAttributedLabel 39 | Type 40 | PSGroupSpecifier 41 | 42 | 43 | FooterText 44 | Generated by CocoaPods - http://cocoapods.org 45 | Title 46 | 47 | Type 48 | PSGroupSpecifier 49 | 50 | 51 | StringsTable 52 | Acknowledgements 53 | Title 54 | Acknowledgements 55 | 56 | 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | TTCounterLabel 2 | ============== 3 | A custom UILabel that acts as a time counter, counting up or down and formatting the string to hours, minutes, seconds and milliseconds. Designed to accept a value in milliseconds that is then displayed it in a time friendly format. Currently the controls supports up-to a maximum value of 99 hours 59 minutes 59 seconds and 999 milliseconds, which should be enough for most uses. The control automatically removes any leading zeros and centralises the result. It also supports different fonts for each unit division. 4 | 5 | ![Alt text](/screenshot.PNG "TTCounterLabel") 6 | 7 | Setup 8 | ----- 9 | 10 | **Installing with [CocoaPods](http://cocoapods.org)** 11 | 12 | If you're unfamiliar with CocoaPods there is a great tutorial [here](http://www.raywenderlich.com/12139/introduction-to-cocoapods) to get you up to speed. 13 | 14 | 1. In Terminal navigate to the root of your project. 15 | 2. Run 'touch Podfile' to create the Podfile. 16 | 3. Open the Podfile using 'open -e Podfile' 17 | 4. Add the pod `TTCounterLabel` to your [Podfile](https://github.com/CocoaPods/CocoaPods/wiki/A-Podfile). 18 | 19 | platform :ios 20 | pod 'TTCounterLabel' 21 | 22 | 5. Run `pod install`. 23 | 6. Open your app's `.xcworkspace` file to launch Xcode and start using the control! 24 | 25 | **Installing manually from GitHub** 26 | 27 | 1. Download the `TTCounterLabel.h` and `TTcounterLabel.m` files and add them to your Xcode project. 28 | 2. `#import TTCounterLabel.h` wherever you need it. 29 | 3. Follow the included sample project to get started. 30 | 31 | **Running the sample project** 32 | 33 | Check out the [sample project](https://github.com/TriggerTrap/TTCounterLabel/tree/master/Sample) included in the repository. Just open the '.xcworkspace' file in the Sample folder and the project should build correctly. 34 | 35 | Usage 36 | ----- 37 | 38 | 1. Add a normal Label control to your storyboard 39 | 2. In the Identity Inspector for the label, set the Custom Class to TTCounterLabel 40 | 3. Implement the TTCounterLabelDelegate interface to receive callbacks from the label 41 | 4. Set the label's countDirection to one of kCountDirectionDown or kCountDirectionUp 42 | 5. call [label setStartValue:<time in ms>] to set the start value (important in the case of a downward counter). 43 | 6. Use [label start] and [label stop] to start and stop the counter. 44 | 7. The counter calls back on the contdownDidEnd method on the class that implements TTCounterLabelDelegate. The source parameter will be a reference to the label object that fired the event. 45 | 46 | Author(s) 47 | ------- 48 | 49 | [Triggertrap Limited](https://github.com/TriggerTrap) 50 | 51 | [Ross Gibson](https://github.com/Ross-Gibson) 52 | 53 | [Valentin Kalchev](https://github.com/Valentin-Kalchev) 54 | 55 | Licence 56 | ------- 57 | 58 | Distributed under the MIT License. 59 | -------------------------------------------------------------------------------- /Sample/Pods/Pods-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 5 | > "$RESOURCES_TO_COPY" 6 | 7 | install_resource() 8 | { 9 | case $1 in 10 | *.storyboard) 11 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile ${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .storyboard`.storyboardc ${PODS_ROOT}/$1 --sdk ${SDKROOT}" 12 | ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .storyboard`.storyboardc" "${PODS_ROOT}/$1" --sdk "${SDKROOT}" 13 | ;; 14 | *.xib) 15 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile ${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .xib`.nib ${PODS_ROOT}/$1 --sdk ${SDKROOT}" 16 | ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .xib`.nib" "${PODS_ROOT}/$1" --sdk "${SDKROOT}" 17 | ;; 18 | *.framework) 19 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 20 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 21 | echo "rsync -av ${PODS_ROOT}/$1 ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 22 | rsync -av "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 23 | ;; 24 | *.xcdatamodel) 25 | echo "xcrun momc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1"`.mom\"" 26 | xcrun momc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodel`.mom" 27 | ;; 28 | *.xcdatamodeld) 29 | echo "xcrun momc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodeld`.momd\"" 30 | xcrun momc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodeld`.momd" 31 | ;; 32 | *.xcassets) 33 | ;; 34 | /*) 35 | echo "$1" 36 | echo "$1" >> "$RESOURCES_TO_COPY" 37 | ;; 38 | *) 39 | echo "${PODS_ROOT}/$1" 40 | echo "${PODS_ROOT}/$1" >> "$RESOURCES_TO_COPY" 41 | ;; 42 | esac 43 | } 44 | 45 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 46 | if [[ "${ACTION}" == "install" ]]; then 47 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 48 | fi 49 | rm -f "$RESOURCES_TO_COPY" 50 | 51 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ `xcrun --find actool` ] && [ `find . -name '*.xcassets' | wc -l` -ne 0 ] 52 | then 53 | case "${TARGETED_DEVICE_FAMILY}" in 54 | 1,2) 55 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 56 | ;; 57 | 1) 58 | TARGET_DEVICE_ARGS="--target-device iphone" 59 | ;; 60 | 2) 61 | TARGET_DEVICE_ARGS="--target-device ipad" 62 | ;; 63 | *) 64 | TARGET_DEVICE_ARGS="--target-device mac" 65 | ;; 66 | esac 67 | find "${PWD}" -name "*.xcassets" -print0 | xargs -0 actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${IPHONEOS_DEPLOYMENT_TARGET}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 68 | fi 69 | -------------------------------------------------------------------------------- /Sample/Sample/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // Sample 4 | // 5 | // Created by Ross Gibson on 15/10/2013. 6 | // Copyright (c) 2013 Triggertrap. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | 11 | #import "TTCounterLabel.h" 12 | 13 | typedef NS_ENUM(NSInteger, kTTCounter){ 14 | kTTCounterRunning = 0, 15 | kTTCounterStopped, 16 | kTTCounterReset, 17 | kTTCounterEnded 18 | }; 19 | 20 | @interface ViewController () { 21 | IBOutlet TTCounterLabel *_counterLabel; 22 | IBOutlet UIButton *_startStopButton; 23 | IBOutlet UIButton *_resetButton; 24 | } 25 | 26 | @end 27 | 28 | @implementation ViewController 29 | 30 | #pragma mark - Lifecycle 31 | 32 | - (void)viewDidLoad { 33 | [super viewDidLoad]; 34 | // Do any additional setup after loading the view, typically from a nib. 35 | 36 | /* 37 | // Uncomment this code to use the label as a count down timer 38 | self.counterLabel.countDirection = kCountDirectionDown; 39 | [self.counterLabel setStartValue:60000]; 40 | self.counterLabel.countdownDelegate = self; 41 | */ 42 | 43 | // Optional 44 | [self customiseAppearance]; 45 | } 46 | 47 | - (void)didReceiveMemoryWarning { 48 | [super didReceiveMemoryWarning]; 49 | // Dispose of any resources that can be recreated. 50 | } 51 | 52 | #pragma mark - IBActions 53 | 54 | - (IBAction)startStopTapped:(id)sender { 55 | if (_counterLabel.isRunning) { 56 | [_counterLabel stop]; 57 | 58 | [self updateUIForState:kTTCounterStopped withSource:_counterLabel]; 59 | } else { 60 | [_counterLabel start]; 61 | 62 | [self updateUIForState:kTTCounterRunning withSource:_counterLabel]; 63 | } 64 | } 65 | 66 | - (IBAction)resetTapped:(id)sender { 67 | [_counterLabel reset]; 68 | 69 | [self updateUIForState:kTTCounterReset withSource:_counterLabel]; 70 | } 71 | 72 | #pragma mark - Private 73 | 74 | - (void)customiseAppearance { 75 | [_counterLabel setBoldFont:[UIFont fontWithName:@"HelveticaNeue-Medium" size:55]]; 76 | [_counterLabel setRegularFont:[UIFont fontWithName:@"HelveticaNeue-UltraLight" size:55]]; 77 | 78 | // The font property of the label is used as the font for H,M,S and MS 79 | [_counterLabel setFont:[UIFont fontWithName:@"HelveticaNeue-UltraLight" size:25]]; 80 | 81 | // Default label properties 82 | _counterLabel.textColor = [UIColor darkGrayColor]; 83 | 84 | // After making any changes we need to call update appearance 85 | [_counterLabel updateApperance]; 86 | } 87 | 88 | - (void)updateUIForState:(NSInteger)state withSource:(TTCounterLabel *)label { 89 | switch (state) { 90 | case kTTCounterRunning: 91 | [_startStopButton setTitle:NSLocalizedString(@"Stop", @"Stop") forState:UIControlStateNormal]; 92 | _resetButton.hidden = YES; 93 | break; 94 | 95 | case kTTCounterStopped: 96 | [_startStopButton setTitle:NSLocalizedString(@"Resume", @"Resume") forState:UIControlStateNormal]; 97 | _resetButton.hidden = NO; 98 | break; 99 | 100 | case kTTCounterReset: 101 | [_startStopButton setTitle:NSLocalizedString(@"Start", @"Start") forState:UIControlStateNormal]; 102 | _resetButton.hidden = YES; 103 | _startStopButton.hidden = NO; 104 | break; 105 | 106 | case kTTCounterEnded: 107 | _startStopButton.hidden = YES; 108 | _resetButton.hidden = NO; 109 | break; 110 | 111 | default: 112 | break; 113 | } 114 | } 115 | 116 | #pragma mark - TTCounterLabelDelegate 117 | 118 | - (void)countdownDidEndForSource:(TTCounterLabel *)source { 119 | [self updateUIForState:kTTCounterEnded withSource:source]; 120 | } 121 | 122 | @end 123 | -------------------------------------------------------------------------------- /Sample/Sample/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 31 | 38 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /Sample/Pods/TTTAttributedLabel/README.md: -------------------------------------------------------------------------------- 1 | # TTTAttributedLabel 2 | 3 | **A drop-in replacement for `UILabel` that supports attributes, data detectors, links, and more** 4 | 5 | `TTTAttributedLabel` is a drop-in replacement for `UILabel`, which provides a simple way to performantly render [attributed strings](http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSAttributedString_Class/Reference/Reference.html). As a bonus, it also supports link embedding, both automatically with `UIDataDetectorTypes` and manually by specifying a range for a URL, address, phone number, event, or transit information. 6 | 7 | Even though `NSAttributedString` support was added for UILabel in iOS 6, `TTTAttributedLabel` has several unique features: 8 | 9 | - Compatibility with iOS >= 4.3 10 | - Compatible with arm64 architecture when compiled with `$(ARCHS_STANDARD_INCLUDING_64_BIT)` 11 | - Automatic data detection 12 | - Manual link embedding 13 | - Label style inheritance for attributed strings 14 | 15 | It also includes advanced paragraph style properties: 16 | 17 | - `verticalAlignment` 18 | - `textInsets` 19 | - `firstLineIndent` 20 | - `leading` 21 | - `lineHeightMultiple` 22 | - `shadowRadius` 23 | - `highlightedShadowRadius` / `highlightedShadowOffset` / `highlightedShadowColor` 24 | - `truncationTokenString` 25 | 26 | ## Installation 27 | 28 | [CocoaPods](http://cocoapods.org) is the recommended method of installing TTTAttributedLabel. Simply add the following line to your `Podfile`: 29 | 30 | #### Podfile 31 | 32 | ```ruby 33 | pod 'TTTAttributedLabel' 34 | ``` 35 | 36 | ## Usage 37 | 38 | ``` objective-c 39 | TTTAttributedLabel *label = [[TTTAttributedLabel alloc] initWithFrame:CGRectZero]; 40 | label.font = [UIFont systemFontOfSize:14]; 41 | label.textColor = [UIColor darkGrayColor]; 42 | label.lineBreakMode = UILineBreakModeWordWrap; 43 | label.numberOfLines = 0; 44 | 45 | NSString *text = @"Lorem ipsum dolar sit amet"; 46 | [label setText:text afterInheritingLabelAttributesAndConfiguringWithBlock:^ NSMutableAttributedString *(NSMutableAttributedString *mutableAttributedString) { 47 | NSRange boldRange = [[mutableAttributedString string] rangeOfString:@"ipsum dolar" options:NSCaseInsensitiveSearch]; 48 | NSRange strikeRange = [[mutableAttributedString string] rangeOfString:@"sit amet" options:NSCaseInsensitiveSearch]; 49 | 50 | // Core Text APIs use C functions without a direct bridge to UIFont. See Apple's "Core Text Programming Guide" to learn how to configure string attributes. 51 | UIFont *boldSystemFont = [UIFont boldSystemFontOfSize:14]; 52 | CTFontRef font = CTFontCreateWithName((__bridge CFStringRef)boldSystemFont.fontName, boldSystemFont.pointSize, NULL); 53 | if (font) { 54 | [mutableAttributedString addAttribute:(NSString *)kCTFontAttributeName value:(id)font range:boldRange]; 55 | [mutableAttributedString addAttribute:kTTTStrikeOutAttributeName value:[NSNumber numberWithBool:YES] range:strikeRange]; 56 | CFRelease(font); 57 | } 58 | 59 | return mutableAttributedString; 60 | }]; 61 | ``` 62 | 63 | First, we create and configure the label, the same way you would instantiate `UILabel`. Any text properties that are set on the label are inherited as the base attributes when using the `-setText:afterInheritingLabelAttributesAndConfiguringWithBlock:` method. In this example, the substring "ipsum dolar", would appear in bold, such that the label would read "Lorem **ipsum dolar** sit amet", in size 14 Helvetica, with a dark gray color. 64 | 65 | The normal `setText:` setter accepts both `NSString` and `NSAttributedString`; in the latter case, the attributed string is directly set, without inheriting the base style of the label. 66 | 67 | ### Links and Data Detection 68 | 69 | In addition to supporting rich text, `TTTAttributedLabel` allows you to automatically detect links for dates, addresses, links, phone numbers, transit information, or allow you to embed your own. 70 | 71 | ``` objective-c 72 | label.enabledTextCheckingTypes = NSTextCheckingTypeLink; // Automatically detect links when the label text is subsequently changed 73 | label.delegate = self; // Delegate methods are called when the user taps on a link (see `TTTAttributedLabelDelegate` protocol) 74 | 75 | label.text = @"Fork me on GitHub! (http://github.com/mattt/TTTAttributedLabel/)"; // Repository URL will be automatically detected and linked 76 | 77 | NSRange range = [label.text rangeOfString:@"me"]; 78 | [label addLinkToURL:[NSURL URLWithString:@"http://github.com/mattt/"] withRange:range]; // Embedding a custom link in a substring 79 | ``` 80 | 81 | ## Demo 82 | 83 | Build and run the `TTTAttributedLabelExample` project in Xcode to see `TTTAttributedLabel` in action. 84 | 85 | ## Requirements 86 | 87 | `TTTAttributedLabel` is compatible with iOS 4.3+ as a deployment target, but must be compiled using the iOS 6 SDK, or higher. If you get compiler errors for undefined constants, try upgrading to the latest version of Xcode, and updating your project to the recommended build settings. 88 | 89 | `TTTAttributedLabel` also requires the `CoreText` and `Core Graphics` frameworks. If you're installing with CocoaPods these frameworks will automatically be linked for you, otherwise you will have to add them to your project. 90 | 91 | For `arm64` compatibility, you must compile your project with the iOS 7 SDK. 92 | 93 | ## Contact 94 | 95 | Mattt Thompson 96 | 97 | - http://github.com/mattt 98 | - http://twitter.com/mattt 99 | - m@mattt.me 100 | 101 | ## License 102 | 103 | TTTAttributedLabel is available under the MIT license. See the LICENSE file for more info. 104 | -------------------------------------------------------------------------------- /Source/TTCounterLabel.m: -------------------------------------------------------------------------------- 1 | // 2 | // TTCounterLabel.m 3 | // TTCounterLabel 4 | // 5 | // Created by Ross Gibson on 10/10/2013. 6 | // Copyright (c) 2013 Triggertrap. All rights reserved. 7 | // 8 | 9 | #import "TTCounterLabel.h" 10 | 11 | @interface TTCounterLabel () { 12 | 13 | } 14 | 15 | @property (strong, nonatomic) NSString *valueString; 16 | @property (strong, nonatomic) NSTimer *clockTimer; 17 | @property (nonatomic, assign) unsigned long long value; 18 | @property (nonatomic, assign) unsigned long long resetValue; 19 | @property (nonatomic, assign) double startTime; 20 | @property (nonatomic, assign) BOOL running; 21 | 22 | @end 23 | 24 | @implementation TTCounterLabel 25 | 26 | #pragma mark - Lifecycle 27 | 28 | - (id)initWithFrame:(CGRect)frame { 29 | self = [super initWithFrame:frame]; 30 | if (self) { 31 | [self commonInit]; 32 | } 33 | return self; 34 | } 35 | 36 | - (id)initWithCoder:(NSCoder *)aDecoder { 37 | self = [super initWithCoder:aDecoder]; 38 | if (self) { 39 | [self commonInit]; 40 | } 41 | return self; 42 | } 43 | 44 | - (void)commonInit { 45 | // Initialization code 46 | self.valueString = @""; 47 | self.textAlignment = NSTextAlignmentCenter; 48 | self.font = [UIFont systemFontOfSize:25]; 49 | self.boldFont = [UIFont boldSystemFontOfSize:55]; 50 | self.regularFont = [UIFont systemFontOfSize:55]; 51 | self.countDirection = kCountDirectionUp; 52 | self.value = 0; 53 | self.startValue = 0; 54 | } 55 | 56 | #pragma mark - Setters 57 | 58 | - (void)setValue:(unsigned long long)value { 59 | 60 | if (value < ULONG_LONG_MAX) { 61 | _value = value; 62 | self.currentValue = _value; 63 | [self updateDisplay]; 64 | } 65 | 66 | //No need to check for greater than ULONG_LONG_MAX due to the fact that the largest number a user can enter is 67 | //99h59m59s = 360000000, ULONG_LONG_MAX is 18446744073709551615 (depends on library and system) and also this number should not be shown 68 | 69 | //Checking with negative numbers is also unnecessary due to the fact that value is passed unsigned 70 | } 71 | 72 | - (void)setStartValue:(unsigned long long)startValue { 73 | if (startValue < ULONG_LONG_MAX) { 74 | _startValue = startValue; 75 | self.resetValue = _startValue; 76 | [self setValue:startValue]; 77 | } else { 78 | // The value is negative, or too large 79 | NSLog(@"Invalid value: startValue of %llu is invalid, either negative or too large", startValue); 80 | 81 | NSLog(@"Setting startValue to the max value of ULONG_LONG_MAX - 1"); 82 | _startValue = (ULONG_LONG_MAX - 1); 83 | self.resetValue = _startValue; 84 | [self setValue:startValue]; 85 | } 86 | } 87 | 88 | #pragma mark - Private 89 | 90 | - (void)updateDisplay { 91 | // The control only displays the 10th of a millisecond, and 50 ms is enough to 92 | // ensure we see the last digit go to zero. 93 | if (self.countDirection == kCountDirectionDown && _value < 50 && self.isRunning) { 94 | [self stop]; 95 | self.valueString = self.displayMode == kDisplayModeFull ? @"00s.00" : @"00s"; 96 | 97 | // Inform any delegates 98 | if (self.countdownDelegate && [self.countdownDelegate respondsToSelector:@selector(countdownDidEndForSource:)]) { 99 | [self.countdownDelegate performSelector:@selector(countdownDidEndForSource:) withObject:self]; 100 | } 101 | } else { 102 | self.valueString = [self timeFormattedStringForValue:_value]; 103 | } 104 | 105 | __weak typeof(self) weakSelf = self; 106 | 107 | [self setText:self.valueString afterInheritingLabelAttributesAndConfiguringWithBlock:^ NSMutableAttributedString *(NSMutableAttributedString *mutableAttributedString) { 108 | 109 | unsigned long long msperhour = 3600000; 110 | unsigned long long hrs = weakSelf.value / msperhour; 111 | 112 | NSString *hoursString = [NSString stringWithFormat:@"%llu", hrs]; 113 | 114 | NSUInteger hrsLength = hoursString.length; 115 | 116 | // Hours 117 | if (weakSelf.value > 3599999) { 118 | // The hours will be bold font, we need to set the font for the mins and secs 119 | CTFontRef font = CTFontCreateWithName((__bridge CFStringRef)weakSelf.regularFont.fontName, weakSelf.regularFont.pointSize, NULL); 120 | 121 | if (font) { 122 | if (hrsLength > 2) { 123 | [mutableAttributedString addAttribute:(NSString *)kCTFontAttributeName value:(__bridge id)font range:NSMakeRange((hrsLength + 2), 2)]; 124 | [mutableAttributedString addAttribute:(NSString *)kCTFontAttributeName value:(__bridge id)font range:NSMakeRange((hrsLength + 6), 2)]; 125 | CFRelease(font); 126 | } else { 127 | [mutableAttributedString addAttribute:(NSString *)kCTFontAttributeName value:(__bridge id)font range:NSMakeRange(4, 2)]; 128 | [mutableAttributedString addAttribute:(NSString *)kCTFontAttributeName value:(__bridge id)font range:NSMakeRange(8, 2)]; 129 | CFRelease(font); 130 | } 131 | } 132 | } 133 | 134 | // Mins 135 | if (weakSelf.value > 59999) { 136 | // The mins will be bold font, we need to set the font for the secs 137 | CTFontRef font = CTFontCreateWithName((__bridge CFStringRef)weakSelf.regularFont.fontName, weakSelf.regularFont.pointSize, NULL); 138 | 139 | if (font) { 140 | [mutableAttributedString addAttribute:(NSString *)kCTFontAttributeName value:(__bridge id)font range:NSMakeRange(4, 2)]; 141 | CFRelease(font); 142 | } 143 | } 144 | 145 | CTFontRef boldFont = CTFontCreateWithName((__bridge CFStringRef)weakSelf.boldFont.fontName, weakSelf.boldFont.pointSize, NULL); 146 | 147 | if (boldFont) { 148 | if (hrsLength > 2) { 149 | [mutableAttributedString addAttribute:(NSString *)kCTFontAttributeName value:(__bridge id)boldFont range:NSMakeRange(0, hrsLength)]; 150 | CFRelease(boldFont); 151 | } else { 152 | [mutableAttributedString addAttribute:(NSString *)kCTFontAttributeName value:(__bridge id)boldFont range:NSMakeRange(0, 2)]; 153 | CFRelease(boldFont); 154 | } 155 | } 156 | 157 | return mutableAttributedString; 158 | }]; 159 | 160 | [self setNeedsDisplay]; 161 | } 162 | 163 | - (void)clockDidTick:(NSTimer *)timer { 164 | double currentTime = CFAbsoluteTimeGetCurrent(); 165 | 166 | double elapsedTime = currentTime - self.startTime; 167 | 168 | // Convert the double to milliseconds 169 | unsigned long long milliSecs = (unsigned long long)(elapsedTime * 1000); 170 | 171 | if (self.countDirection == kCountDirectionDown) { 172 | //Only setValue if the value passed is a positive number or 0 173 | //Trying to pass negative value from subtracting two unsigned variables causes max unsigned long long to be passed 174 | if (_startValue >= milliSecs) { 175 | [self setValue:(_startValue - milliSecs)]; 176 | } 177 | } else { 178 | [self setValue:(_startValue + milliSecs)]; 179 | } 180 | } 181 | 182 | - (NSString *)timeFormattedStringForValue:(unsigned long long)value { 183 | unsigned long long msperhour = 3600000; 184 | unsigned long long mspermin = 60000; 185 | 186 | unsigned long long hrs = value / msperhour; 187 | unsigned long long mins = (value % msperhour) / mspermin; 188 | unsigned long long secs = ((value % msperhour) % mspermin) / 1000; 189 | unsigned long long frac = value % 1000 / 10; 190 | 191 | NSString *formattedString = @""; 192 | 193 | if (hrs == 0) { 194 | if (mins == 0) { 195 | formattedString = self.displayMode == kDisplayModeFull ? [NSString stringWithFormat:@"%02llus.%02llu", secs, frac] : [NSString stringWithFormat:@"%02llus", secs]; 196 | } else { 197 | formattedString = self.displayMode == kDisplayModeFull ? [NSString stringWithFormat:@"%02llum %02llus.%02llu", mins, secs, frac] : [NSString stringWithFormat:@"%02llum %02llus", mins, secs]; 198 | } 199 | } else { 200 | formattedString = self.displayMode == kDisplayModeFull ? [NSString stringWithFormat:@"%02lluh %02llum %02llus.%02llu", hrs, mins, secs, frac] : [NSString stringWithFormat:@"%02lluh %02llum %02llus", hrs, mins, secs]; 201 | } 202 | 203 | return formattedString; 204 | } 205 | 206 | #pragma mark - Public 207 | 208 | - (void)start { 209 | if (self.running) return; 210 | 211 | self.startTime = CFAbsoluteTimeGetCurrent(); 212 | 213 | self.running = YES; 214 | self.isRunning = self.running; 215 | 216 | self.clockTimer = [NSTimer timerWithTimeInterval:0.02 217 | target:self 218 | selector:@selector(clockDidTick:) 219 | userInfo:nil repeats:YES]; 220 | [[NSRunLoop mainRunLoop] addTimer:self.clockTimer forMode:NSRunLoopCommonModes]; 221 | } 222 | 223 | - (void)stop { 224 | if (self.clockTimer) { 225 | [self.clockTimer invalidate]; 226 | self.clockTimer = nil; 227 | 228 | _startValue = self.value; 229 | } 230 | 231 | self.running = NO; 232 | self.isRunning = self.running; 233 | } 234 | 235 | - (void)reset { 236 | [self stop]; 237 | 238 | self.startValue = self.resetValue; 239 | [self setValue:self.resetValue]; 240 | } 241 | 242 | - (void)updateApperance { 243 | self.maximumLineHeight = self.regularFont.pointSize; 244 | [self setValue:_currentValue]; 245 | } 246 | 247 | @end 248 | -------------------------------------------------------------------------------- /Sample/Pods/TTTAttributedLabel/TTTAttributedLabel/TTTAttributedLabel.h: -------------------------------------------------------------------------------- 1 | // TTTAttributedLabel.h 2 | // 3 | // Copyright (c) 2011 Mattt Thompson (http://mattt.me) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | #import 24 | #import 25 | 26 | /** 27 | Vertical alignment for text in a label whose bounds are larger than its text bounds 28 | */ 29 | typedef NS_ENUM(NSInteger, TTTAttributedLabelVerticalAlignment) { 30 | TTTAttributedLabelVerticalAlignmentCenter = 0, 31 | TTTAttributedLabelVerticalAlignmentTop = 1, 32 | TTTAttributedLabelVerticalAlignmentBottom = 2, 33 | }; 34 | 35 | /** 36 | Determines whether the text to which this attribute applies has a strikeout drawn through itself. 37 | */ 38 | extern NSString * const kTTTStrikeOutAttributeName; 39 | 40 | /** 41 | The background fill color. Value must be a `CGColorRef`. Default value is `nil` (no fill). 42 | */ 43 | extern NSString * const kTTTBackgroundFillColorAttributeName; 44 | 45 | /** 46 | The padding for the background fill. Value must be a `UIEdgeInsets`. Default value is `UIEdgeInsetsZero` (no padding). 47 | */ 48 | extern NSString * const kTTTBackgroundFillPaddingAttributeName; 49 | 50 | /** 51 | The background stroke color. Value must be a `CGColorRef`. Default value is `nil` (no stroke). 52 | */ 53 | extern NSString * const kTTTBackgroundStrokeColorAttributeName; 54 | 55 | /** 56 | The background stroke line width. Value must be an `NSNumber`. Default value is `1.0f`. 57 | */ 58 | extern NSString * const kTTTBackgroundLineWidthAttributeName; 59 | 60 | /** 61 | The background corner radius. Value must be an `NSNumber`. Default value is `5.0f`. 62 | */ 63 | extern NSString * const kTTTBackgroundCornerRadiusAttributeName; 64 | 65 | @protocol TTTAttributedLabelDelegate; 66 | 67 | // Override UILabel @property to accept both NSString and NSAttributedString 68 | @protocol TTTAttributedLabel 69 | @property (nonatomic, copy) id text; 70 | @end 71 | 72 | /** 73 | `TTTAttributedLabel` is a drop-in replacement for `UILabel` that supports `NSAttributedString`, as well as automatically-detected and manually-added links to URLs, addresses, phone numbers, and dates. 74 | 75 | # Differences Between `TTTAttributedLabel` and `UILabel` 76 | 77 | For the most part, `TTTAttributedLabel` behaves just like `UILabel`. The following are notable exceptions, in which `TTTAttributedLabel` properties may act differently: 78 | 79 | - `text` - This property now takes an `id` type argument, which can either be a kind of `NSString` or `NSAttributedString` (mutable or immutable in both cases) 80 | - `lineBreakMode` - This property displays only the first line when the value is `UILineBreakModeHeadTruncation`, `UILineBreakModeTailTruncation`, or `UILineBreakModeMiddleTruncation` 81 | - `adjustsFontsizeToFitWidth` - Supported in iOS 5 and greater, this property is effective for any value of `numberOfLines` greater than zero. In iOS 4, setting `numberOfLines` to a value greater than 1 with `adjustsFontSizeToFitWidth` set to `YES` may cause `sizeToFit` to execute indefinitely. 82 | 83 | Any properties affecting text or paragraph styling, such as `firstLineIndent` will only apply when text is set with an `NSString`. If the text is set with an `NSAttributedString`, these properties will not apply. 84 | 85 | ### NSCoding 86 | 87 | `TTTAttributedLabel`, like `UILabel`, conforms to `NSCoding`. However, if the build target is set to less than iOS 6.0, `linkAttributes` and `activeLinkAttributes` will not be encoded or decoded. This is due to an runtime exception thrown when attempting to copy non-object CoreText values in dictionaries. 88 | 89 | @warning Any properties changed on the label after setting the text will not be reflected until a subsequent call to `setText:` or `setText:afterInheritingLabelAttributesAndConfiguringWithBlock:`. This is to say, order of operations matters in this case. For example, if the label text color is originally black when the text is set, changing the text color to red will have no effect on the display of the label until the text is set once again. 90 | 91 | @bug Setting `attributedText` directly is not recommended, as it may cause a crash when attempting to access any links previously set. Instead, call `setText:`, passing an `NSAttributedString`. 92 | */ 93 | @interface TTTAttributedLabel : UILabel 94 | 95 | ///----------------------------- 96 | /// @name Accessing the Delegate 97 | ///----------------------------- 98 | 99 | /** 100 | The receiver's delegate. 101 | 102 | @discussion A `TTTAttributedLabel` delegate responds to messages sent by tapping on links in the label. You can use the delegate to respond to links referencing a URL, address, phone number, date, or date with a specified time zone and duration. 103 | */ 104 | @property (nonatomic, unsafe_unretained) IBOutlet id delegate; 105 | 106 | ///-------------------------------------------- 107 | /// @name Detecting, Accessing, & Styling Links 108 | ///-------------------------------------------- 109 | 110 | /** 111 | @deprecated Use `enabledTextCheckingTypes` property instead. 112 | */ 113 | @property (nonatomic, assign) NSTextCheckingTypes dataDetectorTypes DEPRECATED_ATTRIBUTE; 114 | 115 | /** 116 | A bitmask of `NSTextCheckingType` which are used to automatically detect links in the label text. 117 | 118 | @warning You must specify `enabledTextCheckingTypes` before setting the `text`, with either `setText:` or `setText:afterInheritingLabelAttributesAndConfiguringWithBlock:`. 119 | */ 120 | @property (nonatomic, assign) NSTextCheckingTypes enabledTextCheckingTypes; 121 | 122 | /** 123 | An array of `NSTextCheckingResult` objects for links detected or manually added to the label text. 124 | */ 125 | @property (readonly, nonatomic, strong) NSArray *links; 126 | 127 | /** 128 | A dictionary containing the `NSAttributedString` attributes to be applied to links detected or manually added to the label text. The default link style is blue and underlined. 129 | 130 | @warning You must specify `linkAttributes` before setting autodecting or manually-adding links for these attributes to be applied. 131 | */ 132 | @property (nonatomic, strong) NSDictionary *linkAttributes; 133 | 134 | /** 135 | A dictionary containing the `NSAttributedString` attributes to be applied to links when they are in the active state. Supply `nil` or an empty dictionary to opt out of active link styling. The default active link style is red and underlined. 136 | */ 137 | @property (nonatomic, strong) NSDictionary *activeLinkAttributes; 138 | 139 | /** 140 | A dictionary containing the `NSAttributedString` attributes to be applied to links when they are in the inactive state, which is triggered a change in `tintColor` in iOS 7. Supply `nil` or an empty dictionary to opt out of inactive link styling. The default inactive link style is gray and unadorned. 141 | */ 142 | @property (nonatomic, strong) NSDictionary *inactiveLinkAttributes; 143 | 144 | ///--------------------------------------- 145 | /// @name Acccessing Text Style Attributes 146 | ///--------------------------------------- 147 | 148 | /** 149 | The shadow blur radius for the label. A value of 0 indicates no blur, while larger values produce correspondingly larger blurring. This value must not be negative. The default value is 0. 150 | */ 151 | @property (nonatomic, assign) CGFloat shadowRadius; 152 | 153 | /** 154 | The shadow blur radius for the label when the label's `highlighted` property is `YES`. A value of 0 indicates no blur, while larger values produce correspondingly larger blurring. This value must not be negative. The default value is 0. 155 | */ 156 | @property (nonatomic, assign) CGFloat highlightedShadowRadius; 157 | /** 158 | The shadow offset for the label when the label's `highlighted` property is `YES`. A size of {0, 0} indicates no offset, with positive values extending down and to the right. The default size is {0, 0}. 159 | */ 160 | @property (nonatomic, assign) CGSize highlightedShadowOffset; 161 | /** 162 | The shadow color for the label when the label's `highlighted` property is `YES`. The default value is `nil` (no shadow color). 163 | */ 164 | @property (nonatomic, strong) UIColor *highlightedShadowColor; 165 | 166 | /** 167 | The amount to kern the next character. Default is standard kerning. If this attribute is set to 0.0, no kerning is done at all. 168 | */ 169 | @property (nonatomic, assign) CGFloat kern; 170 | 171 | ///-------------------------------------------- 172 | /// @name Acccessing Paragraph Style Attributes 173 | ///-------------------------------------------- 174 | 175 | /** 176 | The distance, in points, from the leading margin of a frame to the beginning of the paragraph's first line. This value is always nonnegative, and is 0.0 by default. 177 | */ 178 | @property (nonatomic, assign) CGFloat firstLineIndent; 179 | 180 | /** 181 | The space in points added between lines within the paragraph. This value is always nonnegative and is 0.0 by default. 182 | */ 183 | @property (nonatomic, assign) CGFloat leading; 184 | 185 | /** 186 | The minimum line height within the paragraph. If the value is 0.0, the minimum line height is set to the line height of the `font`. 0.0 by default. 187 | */ 188 | @property (nonatomic, assign) CGFloat minimumLineHeight; 189 | 190 | /** 191 | The maximum line height within the paragraph. If the value is 0.0, the maximum line height is set to the line height of the `font`. 0.0 by default. 192 | */ 193 | @property (nonatomic, assign) CGFloat maximumLineHeight; 194 | 195 | /** 196 | The line height multiple. This value is 1.0 by default. 197 | */ 198 | @property (nonatomic, assign) CGFloat lineHeightMultiple; 199 | 200 | /** 201 | The distance, in points, from the margin to the text container. This value is `UIEdgeInsetsZero` by default. 202 | 203 | @discussion The `UIEdgeInset` members correspond to paragraph style properties rather than a particular geometry, and can change depending on the writing direction. 204 | 205 | ## `UIEdgeInset` Member Correspondence With `CTParagraphStyleSpecifier` Values: 206 | 207 | - `top`: `kCTParagraphStyleSpecifierParagraphSpacingBefore` 208 | - `left`: `kCTParagraphStyleSpecifierHeadIndent` 209 | - `bottom`: `kCTParagraphStyleSpecifierParagraphSpacing` 210 | - `right`: `kCTParagraphStyleSpecifierTailIndent` 211 | 212 | */ 213 | @property (nonatomic, assign) UIEdgeInsets textInsets; 214 | 215 | /** 216 | The vertical text alignment for the label, for when the frame size is greater than the text rect size. The vertical alignment is `TTTAttributedLabelVerticalAlignmentCenter` by default. 217 | */ 218 | @property (nonatomic, assign) TTTAttributedLabelVerticalAlignment verticalAlignment; 219 | 220 | ///-------------------------------------------- 221 | /// @name Accessing Truncation Token Appearance 222 | ///-------------------------------------------- 223 | 224 | /** 225 | The truncation token that appears at the end of the truncated line. `nil` by default. 226 | 227 | @discussion When truncation is enabled for the label, by setting `lineBreakMode` to either `UILineBreakModeHeadTruncation`, `UILineBreakModeTailTruncation`, or `UILineBreakModeMiddleTruncation`, the token used to terminate the truncated line will be `truncationTokenString` if defined, otherwise the Unicode Character 'HORIZONTAL ELLIPSIS' (U+2026). 228 | */ 229 | @property (nonatomic, strong) NSString *truncationTokenString; 230 | 231 | /** 232 | The attributes to apply to the truncation token at the end of a truncated line. If unspecified, attributes will be inherited from the preceding character. 233 | */ 234 | @property (nonatomic, strong) NSDictionary *truncationTokenStringAttributes; 235 | 236 | 237 | ///-------------------------------------------- 238 | /// @name Calculating Size of Attributed String 239 | ///-------------------------------------------- 240 | 241 | /** 242 | Calculate and return the size that best fits an attributed string, given the specified constraints on size and number of lines. 243 | 244 | @param attributedString The attributed string. 245 | @param size The maximum dimensions used to calculate size. 246 | @param numberOfLines The maximum number of lines in the text to draw, if the constraining size cannot accomodate the full attributed string. 247 | 248 | @return The size that fits the attributed string within the specified constraints. 249 | */ 250 | + (CGSize)sizeThatFitsAttributedString:(NSAttributedString *)attributedString 251 | withConstraints:(CGSize)size 252 | limitedToNumberOfLines:(NSUInteger)numberOfLines; 253 | 254 | ///---------------------------------- 255 | /// @name Setting the Text Attributes 256 | ///---------------------------------- 257 | 258 | /** 259 | Sets the text displayed by the label. 260 | 261 | @param text An `NSString` or `NSAttributedString` object to be displayed by the label. If the specified text is an `NSString`, the label will display the text like a `UILabel`, inheriting the text styles of the label. If the specified text is an `NSAttributedString`, the label text styles will be overridden by the styles specified in the attributed string. 262 | 263 | @discussion This method overrides `UILabel -setText:` to accept both `NSString` and `NSAttributedString` objects. This string is `nil` by default. 264 | */ 265 | - (void)setText:(id)text; 266 | 267 | /** 268 | Sets the text displayed by the label, after configuring an attributed string containing the text attributes inherited from the label in a block. 269 | 270 | @param text An `NSString` or `NSAttributedString` object to be displayed by the label. 271 | @param block A block object that returns an `NSMutableAttributedString` object and takes a single argument, which is an `NSMutableAttributedString` object with the text from the first parameter, and the text attributes inherited from the label text styles. For example, if you specified the `font` of the label to be `[UIFont boldSystemFontOfSize:14]` and `textColor` to be `[UIColor redColor]`, the `NSAttributedString` argument of the block would be contain the `NSAttributedString` attribute equivalents of those properties. In this block, you can set further attributes on particular ranges. 272 | 273 | @discussion This string is `nil` by default. 274 | */ 275 | - (void)setText:(id)text 276 | afterInheritingLabelAttributesAndConfiguringWithBlock:(NSMutableAttributedString *(^)(NSMutableAttributedString *mutableAttributedString))block; 277 | 278 | ///---------------------------------- 279 | /// @name Accessing the Text Attributes 280 | ///---------------------------------- 281 | 282 | /** 283 | A copy of the label's current attributedText. This returns `nil` if an attributed string has never been set on the label. 284 | */ 285 | @property (readwrite, nonatomic, copy) NSAttributedString *attributedText; 286 | 287 | ///------------------- 288 | /// @name Adding Links 289 | ///------------------- 290 | 291 | /** 292 | Adds a link to an `NSTextCheckingResult`. 293 | 294 | @param result An `NSTextCheckingResult` representing the link's location and type. 295 | */ 296 | - (void)addLinkWithTextCheckingResult:(NSTextCheckingResult *)result; 297 | 298 | /** 299 | Adds a link to an `NSTextCheckingResult`. 300 | 301 | @param result An `NSTextCheckingResult` representing the link's location and type. 302 | @param attributes The attributes to be added to the text in the range of the specified link. If `nil`, no attributes are added. 303 | */ 304 | - (void)addLinkWithTextCheckingResult:(NSTextCheckingResult *)result 305 | attributes:(NSDictionary *)attributes; 306 | 307 | /** 308 | Adds a link to a URL for a specified range in the label text. 309 | 310 | @param url The url to be linked to 311 | @param range The range in the label text of the link. The range must not exceed the bounds of the receiver. 312 | */ 313 | - (void)addLinkToURL:(NSURL *)url 314 | withRange:(NSRange)range; 315 | 316 | /** 317 | Adds a link to an address for a specified range in the label text. 318 | 319 | @param addressComponents A dictionary of address components for the address to be linked to 320 | @param range The range in the label text of the link. The range must not exceed the bounds of the receiver. 321 | 322 | @discussion The address component dictionary keys are described in `NSTextCheckingResult`'s "Keys for Address Components." 323 | */ 324 | - (void)addLinkToAddress:(NSDictionary *)addressComponents 325 | withRange:(NSRange)range; 326 | 327 | /** 328 | Adds a link to a phone number for a specified range in the label text. 329 | 330 | @param phoneNumber The phone number to be linked to. 331 | @param range The range in the label text of the link. The range must not exceed the bounds of the receiver. 332 | */ 333 | - (void)addLinkToPhoneNumber:(NSString *)phoneNumber 334 | withRange:(NSRange)range; 335 | 336 | /** 337 | Adds a link to a date for a specified range in the label text. 338 | 339 | @param date The date to be linked to. 340 | @param range The range in the label text of the link. The range must not exceed the bounds of the receiver. 341 | */ 342 | - (void)addLinkToDate:(NSDate *)date 343 | withRange:(NSRange)range; 344 | 345 | /** 346 | Adds a link to a date with a particular time zone and duration for a specified range in the label text. 347 | 348 | @param date The date to be linked to. 349 | @param timeZone The time zone of the specified date. 350 | @param duration The duration, in seconds from the specified date. 351 | @param range The range in the label text of the link. The range must not exceed the bounds of the receiver. 352 | */ 353 | - (void)addLinkToDate:(NSDate *)date 354 | timeZone:(NSTimeZone *)timeZone 355 | duration:(NSTimeInterval)duration 356 | withRange:(NSRange)range; 357 | 358 | /** 359 | Adds a link to transit information for a specified range in the label text. 360 | 361 | @param components A dictionary containing the transit components. The currently supported keys are `NSTextCheckingAirlineKey` and `NSTextCheckingFlightKey`. 362 | @param range The range in the label text of the link. The range must not exceed the bounds of the receiver. 363 | */ 364 | - (void)addLinkToTransitInformation:(NSDictionary *)components 365 | withRange:(NSRange)range; 366 | 367 | @end 368 | 369 | /** 370 | The `TTTAttributedLabelDelegate` protocol defines the messages sent to an attributed label delegate when links are tapped. All of the methods of this protocol are optional. 371 | */ 372 | @protocol TTTAttributedLabelDelegate 373 | 374 | ///----------------------------------- 375 | /// @name Responding to Link Selection 376 | ///----------------------------------- 377 | @optional 378 | 379 | /** 380 | Tells the delegate that the user did select a link to a URL. 381 | 382 | @param label The label whose link was selected. 383 | @param url The URL for the selected link. 384 | */ 385 | - (void)attributedLabel:(TTTAttributedLabel *)label 386 | didSelectLinkWithURL:(NSURL *)url; 387 | 388 | /** 389 | Tells the delegate that the user did select a link to an address. 390 | 391 | @param label The label whose link was selected. 392 | @param addressComponents The components of the address for the selected link. 393 | */ 394 | - (void)attributedLabel:(TTTAttributedLabel *)label 395 | didSelectLinkWithAddress:(NSDictionary *)addressComponents; 396 | 397 | /** 398 | Tells the delegate that the user did select a link to a phone number. 399 | 400 | @param label The label whose link was selected. 401 | @param phoneNumber The phone number for the selected link. 402 | */ 403 | - (void)attributedLabel:(TTTAttributedLabel *)label 404 | didSelectLinkWithPhoneNumber:(NSString *)phoneNumber; 405 | 406 | /** 407 | Tells the delegate that the user did select a link to a date. 408 | 409 | @param label The label whose link was selected. 410 | @param date The datefor the selected link. 411 | */ 412 | - (void)attributedLabel:(TTTAttributedLabel *)label 413 | didSelectLinkWithDate:(NSDate *)date; 414 | 415 | /** 416 | Tells the delegate that the user did select a link to a date with a time zone and duration. 417 | 418 | @param label The label whose link was selected. 419 | @param date The date for the selected link. 420 | @param timeZone The time zone of the date for the selected link. 421 | @param duration The duration, in seconds from the date for the selected link. 422 | */ 423 | - (void)attributedLabel:(TTTAttributedLabel *)label 424 | didSelectLinkWithDate:(NSDate *)date 425 | timeZone:(NSTimeZone *)timeZone 426 | duration:(NSTimeInterval)duration; 427 | 428 | /** 429 | Tells the delegate that the user did select a link to transit information 430 | 431 | @param label The label whose link was selected. 432 | @param components A dictionary containing the transit components. The currently supported keys are `NSTextCheckingAirlineKey` and `NSTextCheckingFlightKey`. 433 | */ 434 | - (void)attributedLabel:(TTTAttributedLabel *)label 435 | didSelectLinkWithTransitInformation:(NSDictionary *)components; 436 | 437 | /** 438 | Tells the delegate that the user did select a link to a text checking result. 439 | 440 | @discussion This method is called if no other delegate method was called, which can occur by either now implementing the method in `TTTAttributedLabelDelegate` corresponding to a particular link, or the link was added by passing an instance of a custom `NSTextCheckingResult` subclass into `-addLinkWithTextCheckingResult:`. 441 | 442 | @param label The label whose link was selected. 443 | @param result The custom text checking result. 444 | */ 445 | - (void)attributedLabel:(TTTAttributedLabel *)label 446 | didSelectLinkWithTextCheckingResult:(NSTextCheckingResult *)result; 447 | 448 | @end 449 | -------------------------------------------------------------------------------- /Sample/Sample.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 81FCEF7ED713493FB88C88ED /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DE5FD8F429DF4A1F9D8375F1 /* libPods.a */; }; 11 | 9E20A977180D3A3A004388AE /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9E20A976180D3A3A004388AE /* Foundation.framework */; }; 12 | 9E20A979180D3A3A004388AE /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9E20A978180D3A3A004388AE /* CoreGraphics.framework */; }; 13 | 9E20A97B180D3A3A004388AE /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9E20A97A180D3A3A004388AE /* UIKit.framework */; }; 14 | 9E20A981180D3A3A004388AE /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 9E20A97F180D3A3A004388AE /* InfoPlist.strings */; }; 15 | 9E20A983180D3A3A004388AE /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 9E20A982180D3A3A004388AE /* main.m */; }; 16 | 9E20A987180D3A3A004388AE /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 9E20A986180D3A3A004388AE /* AppDelegate.m */; }; 17 | 9E20A98A180D3A3A004388AE /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9E20A988180D3A3A004388AE /* Main.storyboard */; }; 18 | 9E20A98D180D3A3A004388AE /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9E20A98C180D3A3A004388AE /* ViewController.m */; }; 19 | 9E20A98F180D3A3A004388AE /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9E20A98E180D3A3A004388AE /* Images.xcassets */; }; 20 | 9E20A996180D3A3A004388AE /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9E20A995180D3A3A004388AE /* XCTest.framework */; }; 21 | 9E20A997180D3A3A004388AE /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9E20A976180D3A3A004388AE /* Foundation.framework */; }; 22 | 9E20A998180D3A3A004388AE /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9E20A97A180D3A3A004388AE /* UIKit.framework */; }; 23 | 9E20A9A0180D3A3A004388AE /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 9E20A99E180D3A3A004388AE /* InfoPlist.strings */; }; 24 | 9E20A9A2180D3A3A004388AE /* SampleTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 9E20A9A1180D3A3A004388AE /* SampleTests.m */; }; 25 | 9EE65BFD18F4090A006BCB4A /* TTCounterLabel.m in Sources */ = {isa = PBXBuildFile; fileRef = 9EE65BFC18F4090A006BCB4A /* TTCounterLabel.m */; }; 26 | /* End PBXBuildFile section */ 27 | 28 | /* Begin PBXContainerItemProxy section */ 29 | 9E20A999180D3A3A004388AE /* PBXContainerItemProxy */ = { 30 | isa = PBXContainerItemProxy; 31 | containerPortal = 9E20A96B180D3A39004388AE /* Project object */; 32 | proxyType = 1; 33 | remoteGlobalIDString = 9E20A972180D3A3A004388AE; 34 | remoteInfo = Sample; 35 | }; 36 | /* End PBXContainerItemProxy section */ 37 | 38 | /* Begin PBXFileReference section */ 39 | 1D5D3A991AD24B6B9DC26299 /* Pods.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.xcconfig; path = Pods/Pods.xcconfig; sourceTree = ""; }; 40 | 9E20A973180D3A3A004388AE /* Sample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Sample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 41 | 9E20A976180D3A3A004388AE /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 42 | 9E20A978180D3A3A004388AE /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 43 | 9E20A97A180D3A3A004388AE /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 44 | 9E20A97E180D3A3A004388AE /* Sample-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Sample-Info.plist"; sourceTree = ""; }; 45 | 9E20A980180D3A3A004388AE /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 46 | 9E20A982180D3A3A004388AE /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 47 | 9E20A984180D3A3A004388AE /* Sample-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Sample-Prefix.pch"; sourceTree = ""; }; 48 | 9E20A985180D3A3A004388AE /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 49 | 9E20A986180D3A3A004388AE /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 50 | 9E20A989180D3A3A004388AE /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 51 | 9E20A98B180D3A3A004388AE /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 52 | 9E20A98C180D3A3A004388AE /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 53 | 9E20A98E180D3A3A004388AE /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 54 | 9E20A994180D3A3A004388AE /* SampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 55 | 9E20A995180D3A3A004388AE /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 56 | 9E20A99D180D3A3A004388AE /* SampleTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "SampleTests-Info.plist"; sourceTree = ""; }; 57 | 9E20A99F180D3A3A004388AE /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 58 | 9E20A9A1180D3A3A004388AE /* SampleTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SampleTests.m; sourceTree = ""; }; 59 | 9EE65BFB18F4090A006BCB4A /* TTCounterLabel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TTCounterLabel.h; path = ../../Source/TTCounterLabel.h; sourceTree = ""; }; 60 | 9EE65BFC18F4090A006BCB4A /* TTCounterLabel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TTCounterLabel.m; path = ../../Source/TTCounterLabel.m; sourceTree = ""; }; 61 | DE5FD8F429DF4A1F9D8375F1 /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; }; 62 | /* End PBXFileReference section */ 63 | 64 | /* Begin PBXFrameworksBuildPhase section */ 65 | 9E20A970180D3A3A004388AE /* Frameworks */ = { 66 | isa = PBXFrameworksBuildPhase; 67 | buildActionMask = 2147483647; 68 | files = ( 69 | 9E20A979180D3A3A004388AE /* CoreGraphics.framework in Frameworks */, 70 | 9E20A97B180D3A3A004388AE /* UIKit.framework in Frameworks */, 71 | 9E20A977180D3A3A004388AE /* Foundation.framework in Frameworks */, 72 | 81FCEF7ED713493FB88C88ED /* libPods.a in Frameworks */, 73 | ); 74 | runOnlyForDeploymentPostprocessing = 0; 75 | }; 76 | 9E20A991180D3A3A004388AE /* Frameworks */ = { 77 | isa = PBXFrameworksBuildPhase; 78 | buildActionMask = 2147483647; 79 | files = ( 80 | 9E20A996180D3A3A004388AE /* XCTest.framework in Frameworks */, 81 | 9E20A998180D3A3A004388AE /* UIKit.framework in Frameworks */, 82 | 9E20A997180D3A3A004388AE /* Foundation.framework in Frameworks */, 83 | ); 84 | runOnlyForDeploymentPostprocessing = 0; 85 | }; 86 | /* End PBXFrameworksBuildPhase section */ 87 | 88 | /* Begin PBXGroup section */ 89 | 9E20A96A180D3A39004388AE = { 90 | isa = PBXGroup; 91 | children = ( 92 | 9E20A97C180D3A3A004388AE /* Sample */, 93 | 9E20A99B180D3A3A004388AE /* SampleTests */, 94 | 9E20A975180D3A3A004388AE /* Frameworks */, 95 | 9E20A974180D3A3A004388AE /* Products */, 96 | 1D5D3A991AD24B6B9DC26299 /* Pods.xcconfig */, 97 | ); 98 | sourceTree = ""; 99 | }; 100 | 9E20A974180D3A3A004388AE /* Products */ = { 101 | isa = PBXGroup; 102 | children = ( 103 | 9E20A973180D3A3A004388AE /* Sample.app */, 104 | 9E20A994180D3A3A004388AE /* SampleTests.xctest */, 105 | ); 106 | name = Products; 107 | sourceTree = ""; 108 | }; 109 | 9E20A975180D3A3A004388AE /* Frameworks */ = { 110 | isa = PBXGroup; 111 | children = ( 112 | 9E20A976180D3A3A004388AE /* Foundation.framework */, 113 | 9E20A978180D3A3A004388AE /* CoreGraphics.framework */, 114 | 9E20A97A180D3A3A004388AE /* UIKit.framework */, 115 | 9E20A995180D3A3A004388AE /* XCTest.framework */, 116 | DE5FD8F429DF4A1F9D8375F1 /* libPods.a */, 117 | ); 118 | name = Frameworks; 119 | sourceTree = ""; 120 | }; 121 | 9E20A97C180D3A3A004388AE /* Sample */ = { 122 | isa = PBXGroup; 123 | children = ( 124 | 9EE65BFE18F40910006BCB4A /* TTCounterLabel */, 125 | 9E20A985180D3A3A004388AE /* AppDelegate.h */, 126 | 9E20A986180D3A3A004388AE /* AppDelegate.m */, 127 | 9E20A988180D3A3A004388AE /* Main.storyboard */, 128 | 9E20A98B180D3A3A004388AE /* ViewController.h */, 129 | 9E20A98C180D3A3A004388AE /* ViewController.m */, 130 | 9E20A98E180D3A3A004388AE /* Images.xcassets */, 131 | 9E20A97D180D3A3A004388AE /* Supporting Files */, 132 | ); 133 | path = Sample; 134 | sourceTree = ""; 135 | }; 136 | 9E20A97D180D3A3A004388AE /* Supporting Files */ = { 137 | isa = PBXGroup; 138 | children = ( 139 | 9E20A97E180D3A3A004388AE /* Sample-Info.plist */, 140 | 9E20A97F180D3A3A004388AE /* InfoPlist.strings */, 141 | 9E20A982180D3A3A004388AE /* main.m */, 142 | 9E20A984180D3A3A004388AE /* Sample-Prefix.pch */, 143 | ); 144 | name = "Supporting Files"; 145 | sourceTree = ""; 146 | }; 147 | 9E20A99B180D3A3A004388AE /* SampleTests */ = { 148 | isa = PBXGroup; 149 | children = ( 150 | 9E20A9A1180D3A3A004388AE /* SampleTests.m */, 151 | 9E20A99C180D3A3A004388AE /* Supporting Files */, 152 | ); 153 | path = SampleTests; 154 | sourceTree = ""; 155 | }; 156 | 9E20A99C180D3A3A004388AE /* Supporting Files */ = { 157 | isa = PBXGroup; 158 | children = ( 159 | 9E20A99D180D3A3A004388AE /* SampleTests-Info.plist */, 160 | 9E20A99E180D3A3A004388AE /* InfoPlist.strings */, 161 | ); 162 | name = "Supporting Files"; 163 | sourceTree = ""; 164 | }; 165 | 9EE65BFE18F40910006BCB4A /* TTCounterLabel */ = { 166 | isa = PBXGroup; 167 | children = ( 168 | 9EE65BFB18F4090A006BCB4A /* TTCounterLabel.h */, 169 | 9EE65BFC18F4090A006BCB4A /* TTCounterLabel.m */, 170 | ); 171 | name = TTCounterLabel; 172 | sourceTree = ""; 173 | }; 174 | /* End PBXGroup section */ 175 | 176 | /* Begin PBXNativeTarget section */ 177 | 9E20A972180D3A3A004388AE /* Sample */ = { 178 | isa = PBXNativeTarget; 179 | buildConfigurationList = 9E20A9A5180D3A3A004388AE /* Build configuration list for PBXNativeTarget "Sample" */; 180 | buildPhases = ( 181 | 952DCD63521546568E39497B /* Check Pods Manifest.lock */, 182 | 9E20A96F180D3A3A004388AE /* Sources */, 183 | 9E20A970180D3A3A004388AE /* Frameworks */, 184 | 9E20A971180D3A3A004388AE /* Resources */, 185 | 8E05745344EC4B688EB5BD06 /* Copy Pods Resources */, 186 | ); 187 | buildRules = ( 188 | ); 189 | dependencies = ( 190 | ); 191 | name = Sample; 192 | productName = Sample; 193 | productReference = 9E20A973180D3A3A004388AE /* Sample.app */; 194 | productType = "com.apple.product-type.application"; 195 | }; 196 | 9E20A993180D3A3A004388AE /* SampleTests */ = { 197 | isa = PBXNativeTarget; 198 | buildConfigurationList = 9E20A9A8180D3A3A004388AE /* Build configuration list for PBXNativeTarget "SampleTests" */; 199 | buildPhases = ( 200 | 9E20A990180D3A3A004388AE /* Sources */, 201 | 9E20A991180D3A3A004388AE /* Frameworks */, 202 | 9E20A992180D3A3A004388AE /* Resources */, 203 | ); 204 | buildRules = ( 205 | ); 206 | dependencies = ( 207 | 9E20A99A180D3A3A004388AE /* PBXTargetDependency */, 208 | ); 209 | name = SampleTests; 210 | productName = SampleTests; 211 | productReference = 9E20A994180D3A3A004388AE /* SampleTests.xctest */; 212 | productType = "com.apple.product-type.bundle.unit-test"; 213 | }; 214 | /* End PBXNativeTarget section */ 215 | 216 | /* Begin PBXProject section */ 217 | 9E20A96B180D3A39004388AE /* Project object */ = { 218 | isa = PBXProject; 219 | attributes = { 220 | LastUpgradeCheck = 0500; 221 | ORGANIZATIONNAME = Triggertrap; 222 | TargetAttributes = { 223 | 9E20A993180D3A3A004388AE = { 224 | TestTargetID = 9E20A972180D3A3A004388AE; 225 | }; 226 | }; 227 | }; 228 | buildConfigurationList = 9E20A96E180D3A39004388AE /* Build configuration list for PBXProject "Sample" */; 229 | compatibilityVersion = "Xcode 3.2"; 230 | developmentRegion = English; 231 | hasScannedForEncodings = 0; 232 | knownRegions = ( 233 | en, 234 | Base, 235 | ); 236 | mainGroup = 9E20A96A180D3A39004388AE; 237 | productRefGroup = 9E20A974180D3A3A004388AE /* Products */; 238 | projectDirPath = ""; 239 | projectRoot = ""; 240 | targets = ( 241 | 9E20A972180D3A3A004388AE /* Sample */, 242 | 9E20A993180D3A3A004388AE /* SampleTests */, 243 | ); 244 | }; 245 | /* End PBXProject section */ 246 | 247 | /* Begin PBXResourcesBuildPhase section */ 248 | 9E20A971180D3A3A004388AE /* Resources */ = { 249 | isa = PBXResourcesBuildPhase; 250 | buildActionMask = 2147483647; 251 | files = ( 252 | 9E20A98F180D3A3A004388AE /* Images.xcassets in Resources */, 253 | 9E20A981180D3A3A004388AE /* InfoPlist.strings in Resources */, 254 | 9E20A98A180D3A3A004388AE /* Main.storyboard in Resources */, 255 | ); 256 | runOnlyForDeploymentPostprocessing = 0; 257 | }; 258 | 9E20A992180D3A3A004388AE /* Resources */ = { 259 | isa = PBXResourcesBuildPhase; 260 | buildActionMask = 2147483647; 261 | files = ( 262 | 9E20A9A0180D3A3A004388AE /* InfoPlist.strings in Resources */, 263 | ); 264 | runOnlyForDeploymentPostprocessing = 0; 265 | }; 266 | /* End PBXResourcesBuildPhase section */ 267 | 268 | /* Begin PBXShellScriptBuildPhase section */ 269 | 8E05745344EC4B688EB5BD06 /* Copy Pods Resources */ = { 270 | isa = PBXShellScriptBuildPhase; 271 | buildActionMask = 2147483647; 272 | files = ( 273 | ); 274 | inputPaths = ( 275 | ); 276 | name = "Copy Pods Resources"; 277 | outputPaths = ( 278 | ); 279 | runOnlyForDeploymentPostprocessing = 0; 280 | shellPath = /bin/sh; 281 | shellScript = "\"${SRCROOT}/Pods/Pods-resources.sh\"\n"; 282 | showEnvVarsInLog = 0; 283 | }; 284 | 952DCD63521546568E39497B /* Check Pods Manifest.lock */ = { 285 | isa = PBXShellScriptBuildPhase; 286 | buildActionMask = 2147483647; 287 | files = ( 288 | ); 289 | inputPaths = ( 290 | ); 291 | name = "Check Pods Manifest.lock"; 292 | outputPaths = ( 293 | ); 294 | runOnlyForDeploymentPostprocessing = 0; 295 | shellPath = /bin/sh; 296 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; 297 | showEnvVarsInLog = 0; 298 | }; 299 | /* End PBXShellScriptBuildPhase section */ 300 | 301 | /* Begin PBXSourcesBuildPhase section */ 302 | 9E20A96F180D3A3A004388AE /* Sources */ = { 303 | isa = PBXSourcesBuildPhase; 304 | buildActionMask = 2147483647; 305 | files = ( 306 | 9E20A98D180D3A3A004388AE /* ViewController.m in Sources */, 307 | 9E20A987180D3A3A004388AE /* AppDelegate.m in Sources */, 308 | 9E20A983180D3A3A004388AE /* main.m in Sources */, 309 | 9EE65BFD18F4090A006BCB4A /* TTCounterLabel.m in Sources */, 310 | ); 311 | runOnlyForDeploymentPostprocessing = 0; 312 | }; 313 | 9E20A990180D3A3A004388AE /* Sources */ = { 314 | isa = PBXSourcesBuildPhase; 315 | buildActionMask = 2147483647; 316 | files = ( 317 | 9E20A9A2180D3A3A004388AE /* SampleTests.m in Sources */, 318 | ); 319 | runOnlyForDeploymentPostprocessing = 0; 320 | }; 321 | /* End PBXSourcesBuildPhase section */ 322 | 323 | /* Begin PBXTargetDependency section */ 324 | 9E20A99A180D3A3A004388AE /* PBXTargetDependency */ = { 325 | isa = PBXTargetDependency; 326 | target = 9E20A972180D3A3A004388AE /* Sample */; 327 | targetProxy = 9E20A999180D3A3A004388AE /* PBXContainerItemProxy */; 328 | }; 329 | /* End PBXTargetDependency section */ 330 | 331 | /* Begin PBXVariantGroup section */ 332 | 9E20A97F180D3A3A004388AE /* InfoPlist.strings */ = { 333 | isa = PBXVariantGroup; 334 | children = ( 335 | 9E20A980180D3A3A004388AE /* en */, 336 | ); 337 | name = InfoPlist.strings; 338 | sourceTree = ""; 339 | }; 340 | 9E20A988180D3A3A004388AE /* Main.storyboard */ = { 341 | isa = PBXVariantGroup; 342 | children = ( 343 | 9E20A989180D3A3A004388AE /* Base */, 344 | ); 345 | name = Main.storyboard; 346 | sourceTree = ""; 347 | }; 348 | 9E20A99E180D3A3A004388AE /* InfoPlist.strings */ = { 349 | isa = PBXVariantGroup; 350 | children = ( 351 | 9E20A99F180D3A3A004388AE /* en */, 352 | ); 353 | name = InfoPlist.strings; 354 | sourceTree = ""; 355 | }; 356 | /* End PBXVariantGroup section */ 357 | 358 | /* Begin XCBuildConfiguration section */ 359 | 9E20A9A3180D3A3A004388AE /* Debug */ = { 360 | isa = XCBuildConfiguration; 361 | buildSettings = { 362 | ALWAYS_SEARCH_USER_PATHS = NO; 363 | ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)"; 364 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 365 | CLANG_CXX_LIBRARY = "libc++"; 366 | CLANG_ENABLE_MODULES = YES; 367 | CLANG_ENABLE_OBJC_ARC = YES; 368 | CLANG_WARN_BOOL_CONVERSION = YES; 369 | CLANG_WARN_CONSTANT_CONVERSION = YES; 370 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 371 | CLANG_WARN_EMPTY_BODY = YES; 372 | CLANG_WARN_ENUM_CONVERSION = YES; 373 | CLANG_WARN_INT_CONVERSION = YES; 374 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 375 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 376 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 377 | COPY_PHASE_STRIP = NO; 378 | GCC_C_LANGUAGE_STANDARD = gnu99; 379 | GCC_DYNAMIC_NO_PIC = NO; 380 | GCC_OPTIMIZATION_LEVEL = 0; 381 | GCC_PREPROCESSOR_DEFINITIONS = ( 382 | "DEBUG=1", 383 | "$(inherited)", 384 | ); 385 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 386 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 387 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 388 | GCC_WARN_UNDECLARED_SELECTOR = YES; 389 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 390 | GCC_WARN_UNUSED_FUNCTION = YES; 391 | GCC_WARN_UNUSED_VARIABLE = YES; 392 | IPHONEOS_DEPLOYMENT_TARGET = 6.0; 393 | ONLY_ACTIVE_ARCH = YES; 394 | SDKROOT = iphoneos; 395 | }; 396 | name = Debug; 397 | }; 398 | 9E20A9A4180D3A3A004388AE /* Release */ = { 399 | isa = XCBuildConfiguration; 400 | buildSettings = { 401 | ALWAYS_SEARCH_USER_PATHS = NO; 402 | ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)"; 403 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 404 | CLANG_CXX_LIBRARY = "libc++"; 405 | CLANG_ENABLE_MODULES = YES; 406 | CLANG_ENABLE_OBJC_ARC = YES; 407 | CLANG_WARN_BOOL_CONVERSION = YES; 408 | CLANG_WARN_CONSTANT_CONVERSION = YES; 409 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 410 | CLANG_WARN_EMPTY_BODY = YES; 411 | CLANG_WARN_ENUM_CONVERSION = YES; 412 | CLANG_WARN_INT_CONVERSION = YES; 413 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 414 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 415 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 416 | COPY_PHASE_STRIP = YES; 417 | ENABLE_NS_ASSERTIONS = NO; 418 | GCC_C_LANGUAGE_STANDARD = gnu99; 419 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 420 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 421 | GCC_WARN_UNDECLARED_SELECTOR = YES; 422 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 423 | GCC_WARN_UNUSED_FUNCTION = YES; 424 | GCC_WARN_UNUSED_VARIABLE = YES; 425 | IPHONEOS_DEPLOYMENT_TARGET = 6.0; 426 | SDKROOT = iphoneos; 427 | VALIDATE_PRODUCT = YES; 428 | }; 429 | name = Release; 430 | }; 431 | 9E20A9A6180D3A3A004388AE /* Debug */ = { 432 | isa = XCBuildConfiguration; 433 | baseConfigurationReference = 1D5D3A991AD24B6B9DC26299 /* Pods.xcconfig */; 434 | buildSettings = { 435 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 436 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 437 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 438 | GCC_PREFIX_HEADER = "Sample/Sample-Prefix.pch"; 439 | INFOPLIST_FILE = "Sample/Sample-Info.plist"; 440 | PRODUCT_NAME = "$(TARGET_NAME)"; 441 | WRAPPER_EXTENSION = app; 442 | }; 443 | name = Debug; 444 | }; 445 | 9E20A9A7180D3A3A004388AE /* Release */ = { 446 | isa = XCBuildConfiguration; 447 | baseConfigurationReference = 1D5D3A991AD24B6B9DC26299 /* Pods.xcconfig */; 448 | buildSettings = { 449 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 450 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 451 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 452 | GCC_PREFIX_HEADER = "Sample/Sample-Prefix.pch"; 453 | INFOPLIST_FILE = "Sample/Sample-Info.plist"; 454 | PRODUCT_NAME = "$(TARGET_NAME)"; 455 | WRAPPER_EXTENSION = app; 456 | }; 457 | name = Release; 458 | }; 459 | 9E20A9A9180D3A3A004388AE /* Debug */ = { 460 | isa = XCBuildConfiguration; 461 | buildSettings = { 462 | ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)"; 463 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/Sample.app/Sample"; 464 | FRAMEWORK_SEARCH_PATHS = ( 465 | "$(SDKROOT)/Developer/Library/Frameworks", 466 | "$(inherited)", 467 | "$(DEVELOPER_FRAMEWORKS_DIR)", 468 | ); 469 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 470 | GCC_PREFIX_HEADER = "Sample/Sample-Prefix.pch"; 471 | GCC_PREPROCESSOR_DEFINITIONS = ( 472 | "DEBUG=1", 473 | "$(inherited)", 474 | ); 475 | INFOPLIST_FILE = "SampleTests/SampleTests-Info.plist"; 476 | PRODUCT_NAME = "$(TARGET_NAME)"; 477 | TEST_HOST = "$(BUNDLE_LOADER)"; 478 | WRAPPER_EXTENSION = xctest; 479 | }; 480 | name = Debug; 481 | }; 482 | 9E20A9AA180D3A3A004388AE /* Release */ = { 483 | isa = XCBuildConfiguration; 484 | buildSettings = { 485 | ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)"; 486 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/Sample.app/Sample"; 487 | FRAMEWORK_SEARCH_PATHS = ( 488 | "$(SDKROOT)/Developer/Library/Frameworks", 489 | "$(inherited)", 490 | "$(DEVELOPER_FRAMEWORKS_DIR)", 491 | ); 492 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 493 | GCC_PREFIX_HEADER = "Sample/Sample-Prefix.pch"; 494 | INFOPLIST_FILE = "SampleTests/SampleTests-Info.plist"; 495 | PRODUCT_NAME = "$(TARGET_NAME)"; 496 | TEST_HOST = "$(BUNDLE_LOADER)"; 497 | WRAPPER_EXTENSION = xctest; 498 | }; 499 | name = Release; 500 | }; 501 | /* End XCBuildConfiguration section */ 502 | 503 | /* Begin XCConfigurationList section */ 504 | 9E20A96E180D3A39004388AE /* Build configuration list for PBXProject "Sample" */ = { 505 | isa = XCConfigurationList; 506 | buildConfigurations = ( 507 | 9E20A9A3180D3A3A004388AE /* Debug */, 508 | 9E20A9A4180D3A3A004388AE /* Release */, 509 | ); 510 | defaultConfigurationIsVisible = 0; 511 | defaultConfigurationName = Release; 512 | }; 513 | 9E20A9A5180D3A3A004388AE /* Build configuration list for PBXNativeTarget "Sample" */ = { 514 | isa = XCConfigurationList; 515 | buildConfigurations = ( 516 | 9E20A9A6180D3A3A004388AE /* Debug */, 517 | 9E20A9A7180D3A3A004388AE /* Release */, 518 | ); 519 | defaultConfigurationIsVisible = 0; 520 | defaultConfigurationName = Release; 521 | }; 522 | 9E20A9A8180D3A3A004388AE /* Build configuration list for PBXNativeTarget "SampleTests" */ = { 523 | isa = XCConfigurationList; 524 | buildConfigurations = ( 525 | 9E20A9A9180D3A3A004388AE /* Debug */, 526 | 9E20A9AA180D3A3A004388AE /* Release */, 527 | ); 528 | defaultConfigurationIsVisible = 0; 529 | defaultConfigurationName = Release; 530 | }; 531 | /* End XCConfigurationList section */ 532 | }; 533 | rootObject = 9E20A96B180D3A39004388AE /* Project object */; 534 | } 535 | -------------------------------------------------------------------------------- /Sample/Pods/Pods.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | archiveVersion 6 | 1 7 | classes 8 | 9 | objectVersion 10 | 46 11 | objects 12 | 13 | 0FAC333DC7814B32ADCFA600 14 | 15 | children 16 | 17 | F8251C6EBAAF4717A09FCFE9 18 | 8AACAF38155344508FF332EE 19 | 2623CC7EFBA8475591C7DC47 20 | 195D0F8DB318412E92F3359A 21 | 932E04E508D64FF0B19DDB83 22 | ABB79761E50346F584F8FB81 23 | 24 | isa 25 | PBXGroup 26 | name 27 | Pods 28 | sourceTree 29 | <group> 30 | 31 | 15272E288F204AF4BB24A777 32 | 33 | buildActionMask 34 | 2147483647 35 | files 36 | 37 | 89F13EBEE83144F984DBE80D 38 | 39 | isa 40 | PBXSourcesBuildPhase 41 | runOnlyForDeploymentPostprocessing 42 | 0 43 | 44 | 195D0F8DB318412E92F3359A 45 | 46 | includeInIndex 47 | 1 48 | isa 49 | PBXFileReference 50 | lastKnownFileType 51 | sourcecode.c.objc 52 | path 53 | Pods-dummy.m 54 | sourceTree 55 | <group> 56 | 57 | 1A01BEE4F48C4EBE9B117BFA 58 | 59 | children 60 | 61 | 5640B74EE9784E67AAB4FD30 62 | 63 | isa 64 | PBXGroup 65 | name 66 | Pods 67 | sourceTree 68 | <group> 69 | 70 | 1F9479CF13054955BE4BA817 71 | 72 | buildSettings 73 | 74 | ALWAYS_SEARCH_USER_PATHS 75 | NO 76 | CLANG_CXX_LANGUAGE_STANDARD 77 | gnu++0x 78 | CLANG_CXX_LIBRARY 79 | libc++ 80 | CLANG_ENABLE_MODULES 81 | YES 82 | CLANG_ENABLE_OBJC_ARC 83 | NO 84 | CLANG_WARN_BOOL_CONVERSION 85 | YES 86 | CLANG_WARN_CONSTANT_CONVERSION 87 | YES 88 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE 89 | YES 90 | CLANG_WARN_EMPTY_BODY 91 | YES 92 | CLANG_WARN_ENUM_CONVERSION 93 | YES 94 | CLANG_WARN_INT_CONVERSION 95 | YES 96 | CLANG_WARN_OBJC_ROOT_CLASS 97 | YES 98 | COPY_PHASE_STRIP 99 | YES 100 | GCC_C_LANGUAGE_STANDARD 101 | gnu99 102 | GCC_DYNAMIC_NO_PIC 103 | NO 104 | GCC_OPTIMIZATION_LEVEL 105 | 0 106 | GCC_PREPROCESSOR_DEFINITIONS 107 | 108 | DEBUG=1 109 | $(inherited) 110 | 111 | GCC_SYMBOLS_PRIVATE_EXTERN 112 | NO 113 | GCC_WARN_64_TO_32_BIT_CONVERSION 114 | YES 115 | GCC_WARN_ABOUT_RETURN_TYPE 116 | YES 117 | GCC_WARN_UNDECLARED_SELECTOR 118 | YES 119 | GCC_WARN_UNINITIALIZED_AUTOS 120 | YES 121 | GCC_WARN_UNUSED_FUNCTION 122 | YES 123 | GCC_WARN_UNUSED_VARIABLE 124 | YES 125 | IPHONEOS_DEPLOYMENT_TARGET 126 | 6.0 127 | ONLY_ACTIVE_ARCH 128 | YES 129 | STRIP_INSTALLED_PRODUCT 130 | NO 131 | 132 | isa 133 | XCBuildConfiguration 134 | name 135 | Debug 136 | 137 | 206B9A96F57140FE9A4811B3 138 | 139 | explicitFileType 140 | archive.ar 141 | includeInIndex 142 | 0 143 | isa 144 | PBXFileReference 145 | path 146 | libPods-TTTAttributedLabel.a 147 | sourceTree 148 | BUILT_PRODUCTS_DIR 149 | 150 | 2623CC7EFBA8475591C7DC47 151 | 152 | includeInIndex 153 | 1 154 | isa 155 | PBXFileReference 156 | lastKnownFileType 157 | text.plist.xml 158 | path 159 | Pods-acknowledgements.plist 160 | sourceTree 161 | <group> 162 | 163 | 2A85E980E5F84369BCF01F9F 164 | 165 | includeInIndex 166 | 1 167 | isa 168 | PBXFileReference 169 | lastKnownFileType 170 | sourcecode.c.objc 171 | name 172 | TTTAttributedLabel.m 173 | path 174 | TTTAttributedLabel/TTTAttributedLabel.m 175 | sourceTree 176 | <group> 177 | 178 | 2BB51C3C93FD4314A53128E5 179 | 180 | buildConfigurations 181 | 182 | 78CAEEE15F574B7E9CE0CAD9 183 | 5B7792C8EB534EA096BDF499 184 | 185 | defaultConfigurationIsVisible 186 | 0 187 | defaultConfigurationName 188 | Release 189 | isa 190 | XCConfigurationList 191 | 192 | 2C8A62FEE1D5431994948823 193 | 194 | children 195 | 196 | 4605FA3DB93848BBB3CAB1F0 197 | 7A07633900584476B7683717 198 | 1A01BEE4F48C4EBE9B117BFA 199 | 407FE95282224E8BB69273EE 200 | E4689CFD69F54ADBA955FF98 201 | 202 | isa 203 | PBXGroup 204 | sourceTree 205 | <group> 206 | 207 | 315BB31A36D64315BB5CFA5A 208 | 209 | includeInIndex 210 | 1 211 | isa 212 | PBXFileReference 213 | lastKnownFileType 214 | sourcecode.c.h 215 | path 216 | Pods-TTTAttributedLabel-prefix.pch 217 | sourceTree 218 | <group> 219 | 220 | 3174773CA65C4A6AA4BB5DBB 221 | 222 | explicitFileType 223 | archive.ar 224 | includeInIndex 225 | 0 226 | isa 227 | PBXFileReference 228 | path 229 | libPods.a 230 | sourceTree 231 | BUILT_PRODUCTS_DIR 232 | 233 | 33DD752B2A744DF880C57BDA 234 | 235 | isa 236 | PBXFileReference 237 | lastKnownFileType 238 | wrapper.framework 239 | name 240 | CoreGraphics.framework 241 | path 242 | Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS7.1.sdk/System/Library/Frameworks/CoreGraphics.framework 243 | sourceTree 244 | DEVELOPER_DIR 245 | 246 | 3DE506CB28B44659BB2FF7E8 247 | 248 | isa 249 | PBXFileReference 250 | lastKnownFileType 251 | wrapper.framework 252 | name 253 | CoreText.framework 254 | path 255 | Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS7.1.sdk/System/Library/Frameworks/CoreText.framework 256 | sourceTree 257 | DEVELOPER_DIR 258 | 259 | 407FE95282224E8BB69273EE 260 | 261 | children 262 | 263 | 3174773CA65C4A6AA4BB5DBB 264 | 206B9A96F57140FE9A4811B3 265 | 266 | isa 267 | PBXGroup 268 | name 269 | Products 270 | sourceTree 271 | <group> 272 | 273 | 4605FA3DB93848BBB3CAB1F0 274 | 275 | includeInIndex 276 | 1 277 | isa 278 | PBXFileReference 279 | lastKnownFileType 280 | text 281 | name 282 | Podfile 283 | path 284 | ../Podfile 285 | sourceTree 286 | SOURCE_ROOT 287 | xcLanguageSpecificationIdentifier 288 | xcode.lang.ruby 289 | 290 | 4791781FC7F5475C96369A16 291 | 292 | buildActionMask 293 | 2147483647 294 | files 295 | 296 | 9A5BBDBFEF16474A9B820547 297 | E1FA8EE4DAC94D27AA918379 298 | 299 | isa 300 | PBXFrameworksBuildPhase 301 | runOnlyForDeploymentPostprocessing 302 | 0 303 | 304 | 49FD37371F6C4F29A0952FD8 305 | 306 | includeInIndex 307 | 1 308 | isa 309 | PBXFileReference 310 | lastKnownFileType 311 | text.xcconfig 312 | path 313 | Pods-TTTAttributedLabel-Private.xcconfig 314 | sourceTree 315 | <group> 316 | 317 | 5312CF687578464AA800BC4C 318 | 319 | isa 320 | PBXFileReference 321 | lastKnownFileType 322 | wrapper.framework 323 | name 324 | Foundation.framework 325 | path 326 | Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS7.1.sdk/System/Library/Frameworks/Foundation.framework 327 | sourceTree 328 | DEVELOPER_DIR 329 | 330 | 5640B74EE9784E67AAB4FD30 331 | 332 | children 333 | 334 | BAF824A36F5C4929A3622D72 335 | 2A85E980E5F84369BCF01F9F 336 | FCDAA4F3698E489D91309296 337 | 338 | isa 339 | PBXGroup 340 | name 341 | TTTAttributedLabel 342 | path 343 | TTTAttributedLabel 344 | sourceTree 345 | <group> 346 | 347 | 5A14503822E941B3A955E808 348 | 349 | buildConfigurations 350 | 351 | AF164930A33847DFBE43FE29 352 | 944445C4825641358D282483 353 | 354 | defaultConfigurationIsVisible 355 | 0 356 | defaultConfigurationName 357 | Release 358 | isa 359 | XCConfigurationList 360 | 361 | 5B7792C8EB534EA096BDF499 362 | 363 | baseConfigurationReference 364 | 49FD37371F6C4F29A0952FD8 365 | buildSettings 366 | 367 | ALWAYS_SEARCH_USER_PATHS 368 | NO 369 | ARCHS 370 | $(ARCHS_STANDARD_INCLUDING_64_BIT) 371 | COPY_PHASE_STRIP 372 | YES 373 | DSTROOT 374 | /tmp/xcodeproj.dst 375 | GCC_C_LANGUAGE_STANDARD 376 | gnu99 377 | GCC_PRECOMPILE_PREFIX_HEADER 378 | YES 379 | GCC_PREFIX_HEADER 380 | Pods-TTTAttributedLabel-prefix.pch 381 | GCC_VERSION 382 | com.apple.compilers.llvm.clang.1_0 383 | INSTALL_PATH 384 | $(BUILT_PRODUCTS_DIR) 385 | IPHONEOS_DEPLOYMENT_TARGET 386 | 6.0 387 | OTHER_CFLAGS 388 | 389 | -DNS_BLOCK_ASSERTIONS=1 390 | $(inherited) 391 | 392 | OTHER_CPLUSPLUSFLAGS 393 | 394 | -DNS_BLOCK_ASSERTIONS=1 395 | $(inherited) 396 | 397 | OTHER_LDFLAGS 398 | 399 | PRODUCT_NAME 400 | $(TARGET_NAME) 401 | PUBLIC_HEADERS_FOLDER_PATH 402 | $(TARGET_NAME) 403 | SDKROOT 404 | iphoneos 405 | SKIP_INSTALL 406 | YES 407 | VALIDATE_PRODUCT 408 | YES 409 | 410 | isa 411 | XCBuildConfiguration 412 | name 413 | Release 414 | 415 | 6D0C80BC682C416ABCC8400A 416 | 417 | buildConfigurationList 418 | 5A14503822E941B3A955E808 419 | buildPhases 420 | 421 | 15272E288F204AF4BB24A777 422 | 4791781FC7F5475C96369A16 423 | 424 | buildRules 425 | 426 | dependencies 427 | 428 | C3D15910EE4B427EB753BC1A 429 | 430 | isa 431 | PBXNativeTarget 432 | name 433 | Pods 434 | productName 435 | Pods 436 | productReference 437 | 3174773CA65C4A6AA4BB5DBB 438 | productType 439 | com.apple.product-type.library.static 440 | 441 | 759C258063AB409C8E6C0AB9 442 | 443 | buildSettings 444 | 445 | ALWAYS_SEARCH_USER_PATHS 446 | NO 447 | CLANG_CXX_LANGUAGE_STANDARD 448 | gnu++0x 449 | CLANG_CXX_LIBRARY 450 | libc++ 451 | CLANG_ENABLE_MODULES 452 | YES 453 | CLANG_ENABLE_OBJC_ARC 454 | NO 455 | CLANG_WARN_BOOL_CONVERSION 456 | YES 457 | CLANG_WARN_CONSTANT_CONVERSION 458 | YES 459 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE 460 | YES 461 | CLANG_WARN_EMPTY_BODY 462 | YES 463 | CLANG_WARN_ENUM_CONVERSION 464 | YES 465 | CLANG_WARN_INT_CONVERSION 466 | YES 467 | CLANG_WARN_OBJC_ROOT_CLASS 468 | YES 469 | COPY_PHASE_STRIP 470 | NO 471 | ENABLE_NS_ASSERTIONS 472 | NO 473 | GCC_C_LANGUAGE_STANDARD 474 | gnu99 475 | GCC_WARN_64_TO_32_BIT_CONVERSION 476 | YES 477 | GCC_WARN_ABOUT_RETURN_TYPE 478 | YES 479 | GCC_WARN_UNDECLARED_SELECTOR 480 | YES 481 | GCC_WARN_UNINITIALIZED_AUTOS 482 | YES 483 | GCC_WARN_UNUSED_FUNCTION 484 | YES 485 | GCC_WARN_UNUSED_VARIABLE 486 | YES 487 | IPHONEOS_DEPLOYMENT_TARGET 488 | 6.0 489 | STRIP_INSTALLED_PRODUCT 490 | NO 491 | VALIDATE_PRODUCT 492 | YES 493 | 494 | isa 495 | XCBuildConfiguration 496 | name 497 | Release 498 | 499 | 770253A5595F4CF7B6AAECF0 500 | 501 | fileRef 502 | 5312CF687578464AA800BC4C 503 | isa 504 | PBXBuildFile 505 | 506 | 78CAEEE15F574B7E9CE0CAD9 507 | 508 | baseConfigurationReference 509 | 49FD37371F6C4F29A0952FD8 510 | buildSettings 511 | 512 | ALWAYS_SEARCH_USER_PATHS 513 | NO 514 | ARCHS 515 | $(ARCHS_STANDARD_INCLUDING_64_BIT) 516 | COPY_PHASE_STRIP 517 | NO 518 | DSTROOT 519 | /tmp/xcodeproj.dst 520 | GCC_C_LANGUAGE_STANDARD 521 | gnu99 522 | GCC_DYNAMIC_NO_PIC 523 | NO 524 | GCC_OPTIMIZATION_LEVEL 525 | 0 526 | GCC_PRECOMPILE_PREFIX_HEADER 527 | YES 528 | GCC_PREFIX_HEADER 529 | Pods-TTTAttributedLabel-prefix.pch 530 | GCC_PREPROCESSOR_DEFINITIONS 531 | 532 | DEBUG=1 533 | $(inherited) 534 | 535 | GCC_SYMBOLS_PRIVATE_EXTERN 536 | NO 537 | GCC_VERSION 538 | com.apple.compilers.llvm.clang.1_0 539 | INSTALL_PATH 540 | $(BUILT_PRODUCTS_DIR) 541 | IPHONEOS_DEPLOYMENT_TARGET 542 | 6.0 543 | OTHER_LDFLAGS 544 | 545 | PRODUCT_NAME 546 | $(TARGET_NAME) 547 | PUBLIC_HEADERS_FOLDER_PATH 548 | $(TARGET_NAME) 549 | SDKROOT 550 | iphoneos 551 | SKIP_INSTALL 552 | YES 553 | 554 | isa 555 | XCBuildConfiguration 556 | name 557 | Debug 558 | 559 | 78CF3D80A33E49A1BEDEF615 560 | 561 | fileRef 562 | 2A85E980E5F84369BCF01F9F 563 | isa 564 | PBXBuildFile 565 | settings 566 | 567 | COMPILER_FLAGS 568 | -fobjc-arc -DOS_OBJECT_USE_OBJC=0 569 | 570 | 571 | 79F796E98C614430A15E77A4 572 | 573 | attributes 574 | 575 | LastUpgradeCheck 576 | 0510 577 | 578 | buildConfigurationList 579 | BB6D68149FEB45C6A981F681 580 | compatibilityVersion 581 | Xcode 3.2 582 | developmentRegion 583 | English 584 | hasScannedForEncodings 585 | 0 586 | isa 587 | PBXProject 588 | knownRegions 589 | 590 | en 591 | 592 | mainGroup 593 | 2C8A62FEE1D5431994948823 594 | productRefGroup 595 | 407FE95282224E8BB69273EE 596 | projectDirPath 597 | 598 | projectReferences 599 | 600 | projectRoot 601 | 602 | targets 603 | 604 | 6D0C80BC682C416ABCC8400A 605 | CEF34BA8BB764A9EA455D7C0 606 | 607 | 608 | 7A07633900584476B7683717 609 | 610 | children 611 | 612 | 8EA17E3D59E049FEA746023C 613 | 614 | isa 615 | PBXGroup 616 | name 617 | Frameworks 618 | sourceTree 619 | <group> 620 | 621 | 82DC10E4D5A74B8AB40278A1 622 | 623 | containerPortal 624 | 79F796E98C614430A15E77A4 625 | isa 626 | PBXContainerItemProxy 627 | proxyType 628 | 1 629 | remoteGlobalIDString 630 | CEF34BA8BB764A9EA455D7C0 631 | remoteInfo 632 | Pods-TTTAttributedLabel 633 | 634 | 82E752065B524101A5A7DAE6 635 | 636 | fileRef 637 | 33DD752B2A744DF880C57BDA 638 | isa 639 | PBXBuildFile 640 | 641 | 836CCA77595742578FDB7F33 642 | 643 | fileRef 644 | BAF824A36F5C4929A3622D72 645 | isa 646 | PBXBuildFile 647 | 648 | 89F13EBEE83144F984DBE80D 649 | 650 | fileRef 651 | 195D0F8DB318412E92F3359A 652 | isa 653 | PBXBuildFile 654 | 655 | 8AACAF38155344508FF332EE 656 | 657 | includeInIndex 658 | 1 659 | isa 660 | PBXFileReference 661 | lastKnownFileType 662 | text 663 | path 664 | Pods-acknowledgements.markdown 665 | sourceTree 666 | <group> 667 | 668 | 8EA17E3D59E049FEA746023C 669 | 670 | children 671 | 672 | 33DD752B2A744DF880C57BDA 673 | 3DE506CB28B44659BB2FF7E8 674 | 5312CF687578464AA800BC4C 675 | 676 | isa 677 | PBXGroup 678 | name 679 | iOS 680 | sourceTree 681 | <group> 682 | 683 | 932E04E508D64FF0B19DDB83 684 | 685 | includeInIndex 686 | 1 687 | isa 688 | PBXFileReference 689 | lastKnownFileType 690 | sourcecode.c.h 691 | path 692 | Pods-environment.h 693 | sourceTree 694 | <group> 695 | 696 | 944445C4825641358D282483 697 | 698 | baseConfigurationReference 699 | F8251C6EBAAF4717A09FCFE9 700 | buildSettings 701 | 702 | ALWAYS_SEARCH_USER_PATHS 703 | NO 704 | ARCHS 705 | $(ARCHS_STANDARD_INCLUDING_64_BIT) 706 | COPY_PHASE_STRIP 707 | YES 708 | DSTROOT 709 | /tmp/xcodeproj.dst 710 | GCC_C_LANGUAGE_STANDARD 711 | gnu99 712 | GCC_PRECOMPILE_PREFIX_HEADER 713 | YES 714 | GCC_VERSION 715 | com.apple.compilers.llvm.clang.1_0 716 | INSTALL_PATH 717 | $(BUILT_PRODUCTS_DIR) 718 | IPHONEOS_DEPLOYMENT_TARGET 719 | 6.0 720 | OTHER_CFLAGS 721 | 722 | -DNS_BLOCK_ASSERTIONS=1 723 | $(inherited) 724 | 725 | OTHER_CPLUSPLUSFLAGS 726 | 727 | -DNS_BLOCK_ASSERTIONS=1 728 | $(inherited) 729 | 730 | OTHER_LDFLAGS 731 | 732 | PRODUCT_NAME 733 | $(TARGET_NAME) 734 | PUBLIC_HEADERS_FOLDER_PATH 735 | $(TARGET_NAME) 736 | SDKROOT 737 | iphoneos 738 | SKIP_INSTALL 739 | YES 740 | VALIDATE_PRODUCT 741 | YES 742 | 743 | isa 744 | XCBuildConfiguration 745 | name 746 | Release 747 | 748 | 95AD0CB3A6864210880A1DE6 749 | 750 | buildActionMask 751 | 2147483647 752 | files 753 | 754 | 82E752065B524101A5A7DAE6 755 | B2A95434C361445785C2C001 756 | 770253A5595F4CF7B6AAECF0 757 | 758 | isa 759 | PBXFrameworksBuildPhase 760 | runOnlyForDeploymentPostprocessing 761 | 0 762 | 763 | 9A5BBDBFEF16474A9B820547 764 | 765 | fileRef 766 | 5312CF687578464AA800BC4C 767 | isa 768 | PBXBuildFile 769 | 770 | A3778FBE77E84BC8A7A1173B 771 | 772 | includeInIndex 773 | 1 774 | isa 775 | PBXFileReference 776 | lastKnownFileType 777 | text.xcconfig 778 | path 779 | Pods-TTTAttributedLabel.xcconfig 780 | sourceTree 781 | <group> 782 | 783 | ABB79761E50346F584F8FB81 784 | 785 | includeInIndex 786 | 1 787 | isa 788 | PBXFileReference 789 | lastKnownFileType 790 | text.script.sh 791 | path 792 | Pods-resources.sh 793 | sourceTree 794 | <group> 795 | 796 | AF164930A33847DFBE43FE29 797 | 798 | baseConfigurationReference 799 | F8251C6EBAAF4717A09FCFE9 800 | buildSettings 801 | 802 | ALWAYS_SEARCH_USER_PATHS 803 | NO 804 | ARCHS 805 | $(ARCHS_STANDARD_INCLUDING_64_BIT) 806 | COPY_PHASE_STRIP 807 | NO 808 | DSTROOT 809 | /tmp/xcodeproj.dst 810 | GCC_C_LANGUAGE_STANDARD 811 | gnu99 812 | GCC_DYNAMIC_NO_PIC 813 | NO 814 | GCC_OPTIMIZATION_LEVEL 815 | 0 816 | GCC_PRECOMPILE_PREFIX_HEADER 817 | YES 818 | GCC_PREPROCESSOR_DEFINITIONS 819 | 820 | DEBUG=1 821 | $(inherited) 822 | 823 | GCC_SYMBOLS_PRIVATE_EXTERN 824 | NO 825 | GCC_VERSION 826 | com.apple.compilers.llvm.clang.1_0 827 | INSTALL_PATH 828 | $(BUILT_PRODUCTS_DIR) 829 | IPHONEOS_DEPLOYMENT_TARGET 830 | 6.0 831 | OTHER_LDFLAGS 832 | 833 | PRODUCT_NAME 834 | $(TARGET_NAME) 835 | PUBLIC_HEADERS_FOLDER_PATH 836 | $(TARGET_NAME) 837 | SDKROOT 838 | iphoneos 839 | SKIP_INSTALL 840 | YES 841 | 842 | isa 843 | XCBuildConfiguration 844 | name 845 | Debug 846 | 847 | B2A95434C361445785C2C001 848 | 849 | fileRef 850 | 3DE506CB28B44659BB2FF7E8 851 | isa 852 | PBXBuildFile 853 | 854 | B576B829D6FE469A805EE008 855 | 856 | buildActionMask 857 | 2147483647 858 | files 859 | 860 | 836CCA77595742578FDB7F33 861 | 862 | isa 863 | PBXHeadersBuildPhase 864 | runOnlyForDeploymentPostprocessing 865 | 0 866 | 867 | B6951314EE50488182467D60 868 | 869 | includeInIndex 870 | 1 871 | isa 872 | PBXFileReference 873 | lastKnownFileType 874 | sourcecode.c.objc 875 | path 876 | Pods-TTTAttributedLabel-dummy.m 877 | sourceTree 878 | <group> 879 | 880 | BAF824A36F5C4929A3622D72 881 | 882 | includeInIndex 883 | 1 884 | isa 885 | PBXFileReference 886 | lastKnownFileType 887 | sourcecode.c.h 888 | name 889 | TTTAttributedLabel.h 890 | path 891 | TTTAttributedLabel/TTTAttributedLabel.h 892 | sourceTree 893 | <group> 894 | 895 | BB6D68149FEB45C6A981F681 896 | 897 | buildConfigurations 898 | 899 | 1F9479CF13054955BE4BA817 900 | 759C258063AB409C8E6C0AB9 901 | 902 | defaultConfigurationIsVisible 903 | 0 904 | defaultConfigurationName 905 | Release 906 | isa 907 | XCConfigurationList 908 | 909 | C3D15910EE4B427EB753BC1A 910 | 911 | isa 912 | PBXTargetDependency 913 | target 914 | CEF34BA8BB764A9EA455D7C0 915 | targetProxy 916 | 82DC10E4D5A74B8AB40278A1 917 | 918 | CEF34BA8BB764A9EA455D7C0 919 | 920 | buildConfigurationList 921 | 2BB51C3C93FD4314A53128E5 922 | buildPhases 923 | 924 | D7F7721A042F4DBFAD27E234 925 | 95AD0CB3A6864210880A1DE6 926 | B576B829D6FE469A805EE008 927 | 928 | buildRules 929 | 930 | dependencies 931 | 932 | isa 933 | PBXNativeTarget 934 | name 935 | Pods-TTTAttributedLabel 936 | productName 937 | Pods-TTTAttributedLabel 938 | productReference 939 | 206B9A96F57140FE9A4811B3 940 | productType 941 | com.apple.product-type.library.static 942 | 943 | D7F7721A042F4DBFAD27E234 944 | 945 | buildActionMask 946 | 2147483647 947 | files 948 | 949 | DEE60BD6756D4D5B80DC1D60 950 | 78CF3D80A33E49A1BEDEF615 951 | 952 | isa 953 | PBXSourcesBuildPhase 954 | runOnlyForDeploymentPostprocessing 955 | 0 956 | 957 | DEE60BD6756D4D5B80DC1D60 958 | 959 | fileRef 960 | B6951314EE50488182467D60 961 | isa 962 | PBXBuildFile 963 | 964 | E1FA8EE4DAC94D27AA918379 965 | 966 | fileRef 967 | 206B9A96F57140FE9A4811B3 968 | isa 969 | PBXBuildFile 970 | 971 | E4689CFD69F54ADBA955FF98 972 | 973 | children 974 | 975 | 0FAC333DC7814B32ADCFA600 976 | 977 | isa 978 | PBXGroup 979 | name 980 | Targets Support Files 981 | sourceTree 982 | <group> 983 | 984 | F8251C6EBAAF4717A09FCFE9 985 | 986 | includeInIndex 987 | 1 988 | isa 989 | PBXFileReference 990 | lastKnownFileType 991 | text.xcconfig 992 | path 993 | Pods.xcconfig 994 | sourceTree 995 | <group> 996 | 997 | FCDAA4F3698E489D91309296 998 | 999 | children 1000 | 1001 | A3778FBE77E84BC8A7A1173B 1002 | 49FD37371F6C4F29A0952FD8 1003 | B6951314EE50488182467D60 1004 | 315BB31A36D64315BB5CFA5A 1005 | 1006 | isa 1007 | PBXGroup 1008 | name 1009 | Support Files 1010 | sourceTree 1011 | SOURCE_ROOT 1012 | 1013 | 1014 | rootObject 1015 | 79F796E98C614430A15E77A4 1016 | 1017 | 1018 | -------------------------------------------------------------------------------- /Sample/Pods/TTTAttributedLabel/TTTAttributedLabel/TTTAttributedLabel.m: -------------------------------------------------------------------------------- 1 | // TTTAttributedLabel.m 2 | // 3 | // Copyright (c) 2011 Mattt Thompson (http://mattt.me) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | #import "TTTAttributedLabel.h" 24 | 25 | #import 26 | #import 27 | 28 | #define kTTTLineBreakWordWrapTextWidthScalingFactor (M_PI / M_E) 29 | 30 | static CGFloat const TTTFLOAT_MAX = 100000; 31 | 32 | NSString * const kTTTStrikeOutAttributeName = @"TTTStrikeOutAttribute"; 33 | NSString * const kTTTBackgroundFillColorAttributeName = @"TTTBackgroundFillColor"; 34 | NSString * const kTTTBackgroundFillPaddingAttributeName = @"TTTBackgroundFillPadding"; 35 | NSString * const kTTTBackgroundStrokeColorAttributeName = @"TTTBackgroundStrokeColor"; 36 | NSString * const kTTTBackgroundLineWidthAttributeName = @"TTTBackgroundLineWidth"; 37 | NSString * const kTTTBackgroundCornerRadiusAttributeName = @"TTTBackgroundCornerRadius"; 38 | 39 | #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 60000 40 | const NSTextAlignment TTTTextAlignmentLeft = NSTextAlignmentLeft; 41 | const NSTextAlignment TTTTextAlignmentCenter = NSTextAlignmentCenter; 42 | const NSTextAlignment TTTTextAlignmentRight = NSTextAlignmentRight; 43 | const NSTextAlignment TTTTextAlignmentJustified = NSTextAlignmentJustified; 44 | const NSTextAlignment TTTTextAlignmentNatural = NSTextAlignmentNatural; 45 | 46 | const NSLineBreakMode TTTLineBreakByWordWrapping = NSLineBreakByWordWrapping; 47 | const NSLineBreakMode TTTLineBreakByCharWrapping = NSLineBreakByCharWrapping; 48 | const NSLineBreakMode TTTLineBreakByClipping = NSLineBreakByClipping; 49 | const NSLineBreakMode TTTLineBreakByTruncatingHead = NSLineBreakByTruncatingHead; 50 | const NSLineBreakMode TTTLineBreakByTruncatingMiddle = NSLineBreakByTruncatingMiddle; 51 | const NSLineBreakMode TTTLineBreakByTruncatingTail = NSLineBreakByTruncatingTail; 52 | 53 | typedef NSTextAlignment TTTTextAlignment; 54 | typedef NSLineBreakMode TTTLineBreakMode; 55 | #else 56 | const UITextAlignment TTTTextAlignmentLeft = NSTextAlignmentLeft; 57 | const UITextAlignment TTTTextAlignmentCenter = NSTextAlignmentCenter; 58 | const UITextAlignment TTTTextAlignmentRight = NSTextAlignmentRight; 59 | const UITextAlignment TTTTextAlignmentJustified = NSTextAlignmentJustified; 60 | const UITextAlignment TTTTextAlignmentNatural = NSTextAlignmentNatural; 61 | 62 | const UITextAlignment TTTLineBreakByWordWrapping = NSLineBreakByWordWrapping; 63 | const UITextAlignment TTTLineBreakByCharWrapping = NSLineBreakByCharWrapping; 64 | const UITextAlignment TTTLineBreakByClipping = NSLineBreakByClipping; 65 | const UITextAlignment TTTLineBreakByTruncatingHead = NSLineBreakByTruncatingHead; 66 | const UITextAlignment TTTLineBreakByTruncatingMiddle = NSLineBreakByTruncatingMiddle; 67 | const UITextAlignment TTTLineBreakByTruncatingTail = NSLineBreakByTruncatingTail; 68 | 69 | typedef UITextAlignment TTTTextAlignment; 70 | typedef UILineBreakMode TTTLineBreakMode; 71 | #endif 72 | 73 | 74 | static inline CTTextAlignment CTTextAlignmentFromTTTTextAlignment(TTTTextAlignment alignment) { 75 | #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 60000 76 | switch (alignment) { 77 | case NSTextAlignmentLeft: return kCTLeftTextAlignment; 78 | case NSTextAlignmentCenter: return kCTCenterTextAlignment; 79 | case NSTextAlignmentRight: return kCTRightTextAlignment; 80 | default: return kCTNaturalTextAlignment; 81 | } 82 | #else 83 | switch (alignment) { 84 | case UITextAlignmentLeft: return kCTLeftTextAlignment; 85 | case UITextAlignmentCenter: return kCTCenterTextAlignment; 86 | case UITextAlignmentRight: return kCTRightTextAlignment; 87 | default: return kCTNaturalTextAlignment; 88 | } 89 | #endif 90 | } 91 | 92 | static inline CTLineBreakMode CTLineBreakModeFromTTTLineBreakMode(TTTLineBreakMode lineBreakMode) { 93 | #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 60000 94 | switch (lineBreakMode) { 95 | case NSLineBreakByWordWrapping: return kCTLineBreakByWordWrapping; 96 | case NSLineBreakByCharWrapping: return kCTLineBreakByCharWrapping; 97 | case NSLineBreakByClipping: return kCTLineBreakByClipping; 98 | case NSLineBreakByTruncatingHead: return kCTLineBreakByTruncatingHead; 99 | case NSLineBreakByTruncatingTail: return kCTLineBreakByTruncatingTail; 100 | case NSLineBreakByTruncatingMiddle: return kCTLineBreakByTruncatingMiddle; 101 | default: return 0; 102 | } 103 | #else 104 | return CTLineBreakModeFromUILineBreakMode(lineBreakMode); 105 | #endif 106 | } 107 | 108 | #if __IPHONE_OS_VERSION_MAX_ALLOWED < 60000 109 | static inline CTLineBreakMode CTLineBreakModeFromUILineBreakMode(UILineBreakMode lineBreakMode) { 110 | #pragma clang diagnostic push 111 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" 112 | switch (lineBreakMode) { 113 | case UILineBreakModeWordWrap: return kCTLineBreakByWordWrapping; 114 | case UILineBreakModeCharacterWrap: return kCTLineBreakByCharWrapping; 115 | case UILineBreakModeClip: return kCTLineBreakByClipping; 116 | case UILineBreakModeHeadTruncation: return kCTLineBreakByTruncatingHead; 117 | case UILineBreakModeTailTruncation: return kCTLineBreakByTruncatingTail; 118 | case UILineBreakModeMiddleTruncation: return kCTLineBreakByTruncatingMiddle; 119 | default: return 0; 120 | } 121 | #pragma clang diagnostic pop 122 | } 123 | #endif 124 | 125 | static inline CGFLOAT_TYPE CGFloat_ceil(CGFLOAT_TYPE cgfloat) { 126 | #if defined(__LP64__) && __LP64__ 127 | return ceil(cgfloat); 128 | #else 129 | return ceilf(cgfloat); 130 | #endif 131 | } 132 | 133 | static inline CGFLOAT_TYPE CGFloat_floor(CGFLOAT_TYPE cgfloat) { 134 | #if defined(__LP64__) && __LP64__ 135 | return floor(cgfloat); 136 | #else 137 | return floorf(cgfloat); 138 | #endif 139 | } 140 | 141 | static inline CGFLOAT_TYPE CGFloat_round(CGFLOAT_TYPE cgfloat) { 142 | #if defined(__LP64__) && __LP64__ 143 | return round(cgfloat); 144 | #else 145 | return roundf(cgfloat); 146 | #endif 147 | } 148 | 149 | static inline NSDictionary * NSAttributedStringAttributesFromLabel(TTTAttributedLabel *label) { 150 | NSMutableDictionary *mutableAttributes = [NSMutableDictionary dictionary]; 151 | 152 | if ([NSMutableParagraphStyle class]) { 153 | [mutableAttributes setObject:label.font forKey:(NSString *)kCTFontAttributeName]; 154 | [mutableAttributes setObject:label.textColor forKey:(NSString *)kCTForegroundColorAttributeName]; 155 | [mutableAttributes setObject:@(label.kern) forKey:(NSString *)kCTKernAttributeName]; 156 | 157 | NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init]; 158 | paragraphStyle.alignment = label.textAlignment; 159 | paragraphStyle.lineSpacing = label.leading; 160 | paragraphStyle.minimumLineHeight = (label.minimumLineHeight > 0 ? label.minimumLineHeight * label.lineHeightMultiple : label.font.lineHeight); 161 | paragraphStyle.maximumLineHeight = (label.maximumLineHeight > 0 ? label.maximumLineHeight * label.lineHeightMultiple : label.font.lineHeight); 162 | paragraphStyle.lineHeightMultiple = label.lineHeightMultiple; 163 | paragraphStyle.firstLineHeadIndent = label.firstLineIndent; 164 | paragraphStyle.headIndent = paragraphStyle.firstLineHeadIndent; 165 | 166 | if (label.numberOfLines == 1) { 167 | paragraphStyle.lineBreakMode = label.lineBreakMode; 168 | } else { 169 | paragraphStyle.lineBreakMode = NSLineBreakByWordWrapping; 170 | } 171 | 172 | [mutableAttributes setObject:paragraphStyle forKey:(NSString *)kCTParagraphStyleAttributeName]; 173 | } else { 174 | CTFontRef font = CTFontCreateWithName((__bridge CFStringRef)label.font.fontName, label.font.pointSize, NULL); 175 | [mutableAttributes setObject:(__bridge id)font forKey:(NSString *)kCTFontAttributeName]; 176 | CFRelease(font); 177 | 178 | [mutableAttributes setObject:(id)[label.textColor CGColor] forKey:(NSString *)kCTForegroundColorAttributeName]; 179 | [mutableAttributes setObject:@(label.kern) forKey:(NSString *)kCTKernAttributeName]; 180 | 181 | CTTextAlignment alignment = CTTextAlignmentFromTTTTextAlignment(label.textAlignment); 182 | CGFloat lineSpacing = label.leading; 183 | CGFloat minimumLineHeight = label.minimumLineHeight * label.lineHeightMultiple; 184 | CGFloat maximumLineHeight = label.maximumLineHeight * label.lineHeightMultiple; 185 | CGFloat lineSpacingAdjustment = CGFloat_ceil(label.font.lineHeight - label.font.ascender + label.font.descender); 186 | CGFloat lineHeightMultiple = label.lineHeightMultiple; 187 | CGFloat firstLineIndent = label.firstLineIndent; 188 | 189 | CTLineBreakMode lineBreakMode = kCTLineBreakByWordWrapping; 190 | if (label.numberOfLines == 1) { 191 | lineBreakMode = CTLineBreakModeFromTTTLineBreakMode(label.lineBreakMode); 192 | } 193 | 194 | CTParagraphStyleSetting paragraphStyles[12] = { 195 | {.spec = kCTParagraphStyleSpecifierAlignment, .valueSize = sizeof(CTTextAlignment), .value = (const void *)&alignment}, 196 | {.spec = kCTParagraphStyleSpecifierLineBreakMode, .valueSize = sizeof(CTLineBreakMode), .value = (const void *)&lineBreakMode}, 197 | {.spec = kCTParagraphStyleSpecifierLineSpacing, .valueSize = sizeof(CGFloat), .value = (const void *)&lineSpacing}, 198 | {.spec = kCTParagraphStyleSpecifierMinimumLineSpacing, .valueSize = sizeof(CGFloat), .value = (const void *)&minimumLineHeight}, 199 | {.spec = kCTParagraphStyleSpecifierMaximumLineSpacing, .valueSize = sizeof(CGFloat), .value = (const void *)&maximumLineHeight}, 200 | {.spec = kCTParagraphStyleSpecifierLineSpacingAdjustment, .valueSize = sizeof (CGFloat), .value = (const void *)&lineSpacingAdjustment}, 201 | {.spec = kCTParagraphStyleSpecifierLineHeightMultiple, .valueSize = sizeof(CGFloat), .value = (const void *)&lineHeightMultiple}, 202 | {.spec = kCTParagraphStyleSpecifierFirstLineHeadIndent, .valueSize = sizeof(CGFloat), .value = (const void *)&firstLineIndent}, 203 | }; 204 | 205 | CTParagraphStyleRef paragraphStyle = CTParagraphStyleCreate(paragraphStyles, 12); 206 | 207 | [mutableAttributes setObject:(__bridge id)paragraphStyle forKey:(NSString *)kCTParagraphStyleAttributeName]; 208 | 209 | CFRelease(paragraphStyle); 210 | } 211 | 212 | return [NSDictionary dictionaryWithDictionary:mutableAttributes]; 213 | } 214 | 215 | static inline NSAttributedString * NSAttributedStringByScalingFontSize(NSAttributedString *attributedString, CGFloat scale) { 216 | NSMutableAttributedString *mutableAttributedString = [attributedString mutableCopy]; 217 | [mutableAttributedString enumerateAttribute:(NSString *)kCTFontAttributeName inRange:NSMakeRange(0, [mutableAttributedString length]) options:0 usingBlock:^(id value, NSRange range, BOOL * __unused stop) { 218 | UIFont *font = (UIFont *)value; 219 | if (font) { 220 | NSString *fontName; 221 | CGFloat pointSize; 222 | 223 | if ([font isKindOfClass:[UIFont class]]) { 224 | fontName = font.fontName; 225 | pointSize = font.pointSize; 226 | } else { 227 | fontName = (NSString *)CFBridgingRelease(CTFontCopyName((__bridge CTFontRef)font, kCTFontPostScriptNameKey)); 228 | pointSize = CTFontGetSize((__bridge CTFontRef)font); 229 | } 230 | 231 | [mutableAttributedString removeAttribute:(NSString *)kCTFontAttributeName range:range]; 232 | CTFontRef fontRef = CTFontCreateWithName((__bridge CFStringRef)fontName, CGFloat_floor(pointSize * scale), NULL); 233 | [mutableAttributedString addAttribute:(NSString *)kCTFontAttributeName value:(__bridge id)fontRef range:range]; 234 | CFRelease(fontRef); 235 | } 236 | }]; 237 | 238 | return mutableAttributedString; 239 | } 240 | 241 | static inline NSAttributedString * NSAttributedStringBySettingColorFromContext(NSAttributedString *attributedString, UIColor *color) { 242 | if (!color) { 243 | return attributedString; 244 | } 245 | 246 | NSMutableAttributedString *mutableAttributedString = [attributedString mutableCopy]; 247 | [mutableAttributedString enumerateAttribute:(NSString *)kCTForegroundColorFromContextAttributeName inRange:NSMakeRange(0, [mutableAttributedString length]) options:0 usingBlock:^(id value, NSRange range, __unused BOOL *stop) { 248 | BOOL usesColorFromContext = (BOOL)value; 249 | if (usesColorFromContext) { 250 | [mutableAttributedString setAttributes:[NSDictionary dictionaryWithObject:color forKey:(NSString *)kCTForegroundColorAttributeName] range:range]; 251 | [mutableAttributedString removeAttribute:(NSString *)kCTForegroundColorFromContextAttributeName range:range]; 252 | } 253 | }]; 254 | 255 | return mutableAttributedString; 256 | } 257 | 258 | static inline CGSize CTFramesetterSuggestFrameSizeForAttributedStringWithConstraints(CTFramesetterRef framesetter, NSAttributedString *attributedString, CGSize size, NSUInteger numberOfLines) { 259 | CFRange rangeToSize = CFRangeMake(0, (CFIndex)[attributedString length]); 260 | CGSize constraints = CGSizeMake(size.width, TTTFLOAT_MAX); 261 | 262 | if (numberOfLines == 1) { 263 | // If there is one line, the size that fits is the full width of the line 264 | constraints = CGSizeMake(TTTFLOAT_MAX, TTTFLOAT_MAX); 265 | } else if (numberOfLines > 0) { 266 | // If the line count of the label more than 1, limit the range to size to the number of lines that have been set 267 | CGMutablePathRef path = CGPathCreateMutable(); 268 | CGPathAddRect(path, NULL, CGRectMake(0.0f, 0.0f, constraints.width, TTTFLOAT_MAX)); 269 | CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, NULL); 270 | CFArrayRef lines = CTFrameGetLines(frame); 271 | 272 | if (CFArrayGetCount(lines) > 0) { 273 | NSInteger lastVisibleLineIndex = MIN((CFIndex)numberOfLines, CFArrayGetCount(lines)) - 1; 274 | CTLineRef lastVisibleLine = CFArrayGetValueAtIndex(lines, lastVisibleLineIndex); 275 | 276 | CFRange rangeToLayout = CTLineGetStringRange(lastVisibleLine); 277 | rangeToSize = CFRangeMake(0, rangeToLayout.location + rangeToLayout.length); 278 | } 279 | 280 | CFRelease(frame); 281 | CFRelease(path); 282 | } 283 | 284 | CGSize suggestedSize = CTFramesetterSuggestFrameSizeWithConstraints(framesetter, rangeToSize, NULL, constraints, NULL); 285 | 286 | return CGSizeMake(CGFloat_ceil(suggestedSize.width), CGFloat_ceil(suggestedSize.height)); 287 | } 288 | 289 | @interface TTTAttributedLabel () 290 | @property (readwrite, nonatomic, copy) NSAttributedString *inactiveAttributedText; 291 | @property (readwrite, nonatomic, copy) NSAttributedString *renderedAttributedText; 292 | @property (readwrite, nonatomic, strong) NSDataDetector *dataDetector; 293 | @property (readwrite, nonatomic, strong) NSArray *links; 294 | @property (readwrite, nonatomic, strong) NSTextCheckingResult *activeLink; 295 | @end 296 | 297 | @implementation TTTAttributedLabel { 298 | @private 299 | BOOL _needsFramesetter; 300 | CTFramesetterRef _framesetter; 301 | CTFramesetterRef _highlightFramesetter; 302 | } 303 | 304 | @dynamic text; 305 | @synthesize attributedText = _attributedText; 306 | 307 | - (id)initWithFrame:(CGRect)frame { 308 | self = [super initWithFrame:frame]; 309 | if (!self) { 310 | return nil; 311 | } 312 | 313 | [self commonInit]; 314 | 315 | return self; 316 | } 317 | 318 | - (void)commonInit { 319 | self.userInteractionEnabled = YES; 320 | self.multipleTouchEnabled = NO; 321 | 322 | self.textInsets = UIEdgeInsetsZero; 323 | self.lineHeightMultiple = 1.0f; 324 | 325 | self.links = [NSArray array]; 326 | 327 | NSMutableDictionary *mutableLinkAttributes = [NSMutableDictionary dictionary]; 328 | [mutableLinkAttributes setObject:[NSNumber numberWithBool:YES] forKey:(NSString *)kCTUnderlineStyleAttributeName]; 329 | 330 | NSMutableDictionary *mutableActiveLinkAttributes = [NSMutableDictionary dictionary]; 331 | [mutableActiveLinkAttributes setObject:[NSNumber numberWithBool:NO] forKey:(NSString *)kCTUnderlineStyleAttributeName]; 332 | 333 | NSMutableDictionary *mutableInactiveLinkAttributes = [NSMutableDictionary dictionary]; 334 | [mutableInactiveLinkAttributes setObject:[NSNumber numberWithBool:NO] forKey:(NSString *)kCTUnderlineStyleAttributeName]; 335 | 336 | if ([NSMutableParagraphStyle class]) { 337 | [mutableLinkAttributes setObject:[UIColor blueColor] forKey:(NSString *)kCTForegroundColorAttributeName]; 338 | [mutableActiveLinkAttributes setObject:[UIColor redColor] forKey:(NSString *)kCTForegroundColorAttributeName]; 339 | [mutableInactiveLinkAttributes setObject:[UIColor grayColor] forKey:(NSString *)kCTForegroundColorAttributeName]; 340 | 341 | NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init]; 342 | paragraphStyle.lineBreakMode = NSLineBreakByWordWrapping; 343 | 344 | [mutableLinkAttributes setObject:paragraphStyle forKey:(NSString *)kCTParagraphStyleAttributeName]; 345 | [mutableActiveLinkAttributes setObject:paragraphStyle forKey:(NSString *)kCTParagraphStyleAttributeName]; 346 | [mutableInactiveLinkAttributes setObject:paragraphStyle forKey:(NSString *)kCTParagraphStyleAttributeName]; 347 | } else { 348 | [mutableLinkAttributes setObject:(__bridge id)[[UIColor blueColor] CGColor] forKey:(NSString *)kCTForegroundColorAttributeName]; 349 | [mutableActiveLinkAttributes setObject:(__bridge id)[[UIColor redColor] CGColor] forKey:(NSString *)kCTForegroundColorAttributeName]; 350 | [mutableInactiveLinkAttributes setObject:(__bridge id)[[UIColor grayColor] CGColor] forKey:(NSString *)kCTForegroundColorAttributeName]; 351 | 352 | CTLineBreakMode lineBreakMode = kCTLineBreakByWordWrapping; 353 | CTParagraphStyleSetting paragraphStyles[1] = { 354 | {.spec = kCTParagraphStyleSpecifierLineBreakMode, .valueSize = sizeof(CTLineBreakMode), .value = (const void *)&lineBreakMode} 355 | }; 356 | CTParagraphStyleRef paragraphStyle = CTParagraphStyleCreate(paragraphStyles, 1); 357 | 358 | [mutableLinkAttributes setObject:(__bridge id)paragraphStyle forKey:(NSString *)kCTParagraphStyleAttributeName]; 359 | [mutableActiveLinkAttributes setObject:(__bridge id)paragraphStyle forKey:(NSString *)kCTParagraphStyleAttributeName]; 360 | [mutableInactiveLinkAttributes setObject:(__bridge id)paragraphStyle forKey:(NSString *)kCTParagraphStyleAttributeName]; 361 | 362 | CFRelease(paragraphStyle); 363 | } 364 | 365 | self.linkAttributes = [NSDictionary dictionaryWithDictionary:mutableLinkAttributes]; 366 | self.activeLinkAttributes = [NSDictionary dictionaryWithDictionary:mutableActiveLinkAttributes]; 367 | self.inactiveLinkAttributes = [NSDictionary dictionaryWithDictionary:mutableInactiveLinkAttributes]; 368 | } 369 | 370 | - (void)dealloc { 371 | if (_framesetter) { 372 | CFRelease(_framesetter); 373 | } 374 | 375 | if (_highlightFramesetter) { 376 | CFRelease(_highlightFramesetter); 377 | } 378 | } 379 | 380 | #pragma mark - 381 | 382 | + (CGSize)sizeThatFitsAttributedString:(NSAttributedString *)attributedString 383 | withConstraints:(CGSize)size 384 | limitedToNumberOfLines:(NSUInteger)numberOfLines 385 | { 386 | if (!attributedString) { 387 | return CGSizeZero; 388 | } 389 | 390 | CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)attributedString); 391 | 392 | CGSize calculatedSize = CTFramesetterSuggestFrameSizeForAttributedStringWithConstraints(framesetter, attributedString, size, numberOfLines); 393 | 394 | CFRelease(framesetter); 395 | 396 | return calculatedSize; 397 | } 398 | 399 | #pragma mark - 400 | 401 | - (void)setAttributedText:(NSAttributedString *)text { 402 | if ([text isEqualToAttributedString:_attributedText]) { 403 | return; 404 | } 405 | 406 | _attributedText = [text copy]; 407 | 408 | [self setNeedsFramesetter]; 409 | [self setNeedsDisplay]; 410 | 411 | #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 60000 412 | if ([self respondsToSelector:@selector(invalidateIntrinsicContentSize)]) { 413 | [self invalidateIntrinsicContentSize]; 414 | } 415 | #endif 416 | } 417 | 418 | - (void)setNeedsFramesetter { 419 | // Reset the rendered attributed text so it has a chance to regenerate 420 | self.renderedAttributedText = nil; 421 | 422 | _needsFramesetter = YES; 423 | } 424 | 425 | - (CTFramesetterRef)framesetter { 426 | if (_needsFramesetter) { 427 | @synchronized(self) { 428 | CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)self.renderedAttributedText); 429 | [self setFramesetter:framesetter]; 430 | [self setHighlightFramesetter:nil]; 431 | _needsFramesetter = NO; 432 | 433 | if (framesetter) { 434 | CFRelease(framesetter); 435 | } 436 | } 437 | } 438 | 439 | return _framesetter; 440 | } 441 | 442 | - (void)setFramesetter:(CTFramesetterRef)framesetter { 443 | if (framesetter) { 444 | CFRetain(framesetter); 445 | } 446 | 447 | if (_framesetter) { 448 | CFRelease(_framesetter); 449 | } 450 | 451 | _framesetter = framesetter; 452 | } 453 | 454 | - (CTFramesetterRef)highlightFramesetter { 455 | return _highlightFramesetter; 456 | } 457 | 458 | - (void)setHighlightFramesetter:(CTFramesetterRef)highlightFramesetter { 459 | if (highlightFramesetter) { 460 | CFRetain(highlightFramesetter); 461 | } 462 | 463 | if (_highlightFramesetter) { 464 | CFRelease(_highlightFramesetter); 465 | } 466 | 467 | _highlightFramesetter = highlightFramesetter; 468 | } 469 | 470 | - (NSAttributedString *)renderedAttributedText { 471 | if (!_renderedAttributedText) { 472 | self.renderedAttributedText = NSAttributedStringBySettingColorFromContext(self.attributedText, self.textColor); 473 | } 474 | 475 | return _renderedAttributedText; 476 | } 477 | 478 | #pragma mark - 479 | 480 | - (NSTextCheckingTypes)dataDetectorTypes { 481 | return self.enabledTextCheckingTypes; 482 | } 483 | 484 | - (void)setDataDetectorTypes:(NSTextCheckingTypes)dataDetectorTypes { 485 | self.enabledTextCheckingTypes = dataDetectorTypes; 486 | } 487 | 488 | - (void)setEnabledTextCheckingTypes:(NSTextCheckingTypes)enabledTextCheckingTypes { 489 | _enabledTextCheckingTypes = enabledTextCheckingTypes; 490 | 491 | if (self.enabledTextCheckingTypes) { 492 | self.dataDetector = [NSDataDetector dataDetectorWithTypes:self.enabledTextCheckingTypes error:nil]; 493 | } else { 494 | self.dataDetector = nil; 495 | } 496 | } 497 | 498 | - (void)addLinkWithTextCheckingResult:(NSTextCheckingResult *)result 499 | attributes:(NSDictionary *)attributes 500 | { 501 | [self addLinksWithTextCheckingResults:[NSArray arrayWithObject:result] attributes:attributes]; 502 | } 503 | 504 | - (void)addLinksWithTextCheckingResults:(NSArray *)results 505 | attributes:(NSDictionary *)attributes 506 | { 507 | NSMutableArray *mutableLinks = [NSMutableArray arrayWithArray:self.links]; 508 | if (attributes) { 509 | NSMutableAttributedString *mutableAttributedString = [self.attributedText mutableCopy]; 510 | for (NSTextCheckingResult *result in results) { 511 | [mutableAttributedString addAttributes:attributes range:result.range]; 512 | } 513 | 514 | self.attributedText = mutableAttributedString; 515 | [self setNeedsDisplay]; 516 | } 517 | [mutableLinks addObjectsFromArray:results]; 518 | 519 | self.links = [NSArray arrayWithArray:mutableLinks]; 520 | } 521 | 522 | - (void)addLinkWithTextCheckingResult:(NSTextCheckingResult *)result { 523 | [self addLinkWithTextCheckingResult:result attributes:self.linkAttributes]; 524 | } 525 | 526 | - (void)addLinkToURL:(NSURL *)url 527 | withRange:(NSRange)range 528 | { 529 | [self addLinkWithTextCheckingResult:[NSTextCheckingResult linkCheckingResultWithRange:range URL:url]]; 530 | } 531 | 532 | - (void)addLinkToAddress:(NSDictionary *)addressComponents 533 | withRange:(NSRange)range 534 | { 535 | [self addLinkWithTextCheckingResult:[NSTextCheckingResult addressCheckingResultWithRange:range components:addressComponents]]; 536 | } 537 | 538 | - (void)addLinkToPhoneNumber:(NSString *)phoneNumber 539 | withRange:(NSRange)range 540 | { 541 | [self addLinkWithTextCheckingResult:[NSTextCheckingResult phoneNumberCheckingResultWithRange:range phoneNumber:phoneNumber]]; 542 | } 543 | 544 | - (void)addLinkToDate:(NSDate *)date 545 | withRange:(NSRange)range 546 | { 547 | [self addLinkWithTextCheckingResult:[NSTextCheckingResult dateCheckingResultWithRange:range date:date]]; 548 | } 549 | 550 | - (void)addLinkToDate:(NSDate *)date 551 | timeZone:(NSTimeZone *)timeZone 552 | duration:(NSTimeInterval)duration 553 | withRange:(NSRange)range 554 | { 555 | [self addLinkWithTextCheckingResult:[NSTextCheckingResult dateCheckingResultWithRange:range date:date timeZone:timeZone duration:duration]]; 556 | } 557 | 558 | - (void)addLinkToTransitInformation:(NSDictionary *)components 559 | withRange:(NSRange)range 560 | { 561 | [self addLinkWithTextCheckingResult:[NSTextCheckingResult transitInformationCheckingResultWithRange:range components:components]]; 562 | } 563 | 564 | 565 | #pragma mark - 566 | 567 | - (NSTextCheckingResult *)linkAtCharacterIndex:(CFIndex)idx { 568 | NSEnumerator *enumerator = [self.links reverseObjectEnumerator]; 569 | NSTextCheckingResult *result = nil; 570 | while ((result = [enumerator nextObject])) { 571 | if (NSLocationInRange((NSUInteger)idx, result.range)) { 572 | return result; 573 | } 574 | } 575 | 576 | return nil; 577 | } 578 | 579 | - (NSTextCheckingResult *)linkAtPoint:(CGPoint)p { 580 | CFIndex idx = [self characterIndexAtPoint:p]; 581 | 582 | return [self linkAtCharacterIndex:idx]; 583 | } 584 | 585 | - (CFIndex)characterIndexAtPoint:(CGPoint)p { 586 | if (!CGRectContainsPoint(self.bounds, p)) { 587 | return NSNotFound; 588 | } 589 | 590 | CGRect textRect = [self textRectForBounds:self.bounds limitedToNumberOfLines:self.numberOfLines]; 591 | if (!CGRectContainsPoint(textRect, p)) { 592 | return NSNotFound; 593 | } 594 | 595 | // Offset tap coordinates by textRect origin to make them relative to the origin of frame 596 | p = CGPointMake(p.x - textRect.origin.x, p.y - textRect.origin.y); 597 | // Convert tap coordinates (start at top left) to CT coordinates (start at bottom left) 598 | p = CGPointMake(p.x, textRect.size.height - p.y); 599 | 600 | CGMutablePathRef path = CGPathCreateMutable(); 601 | CGPathAddRect(path, NULL, textRect); 602 | CTFrameRef frame = CTFramesetterCreateFrame([self framesetter], CFRangeMake(0, (CFIndex)[self.attributedText length]), path, NULL); 603 | if (frame == NULL) { 604 | CFRelease(path); 605 | return NSNotFound; 606 | } 607 | 608 | CFArrayRef lines = CTFrameGetLines(frame); 609 | NSInteger numberOfLines = self.numberOfLines > 0 ? MIN(self.numberOfLines, CFArrayGetCount(lines)) : CFArrayGetCount(lines); 610 | if (numberOfLines == 0) { 611 | CFRelease(frame); 612 | CFRelease(path); 613 | return NSNotFound; 614 | } 615 | 616 | CFIndex idx = NSNotFound; 617 | 618 | CGPoint lineOrigins[numberOfLines]; 619 | CTFrameGetLineOrigins(frame, CFRangeMake(0, numberOfLines), lineOrigins); 620 | 621 | for (CFIndex lineIndex = 0; lineIndex < numberOfLines; lineIndex++) { 622 | CGPoint lineOrigin = lineOrigins[lineIndex]; 623 | CTLineRef line = CFArrayGetValueAtIndex(lines, lineIndex); 624 | 625 | // Get bounding information of line 626 | CGFloat ascent = 0.0f, descent = 0.0f, leading = 0.0f; 627 | CGFloat width = (CGFloat)CTLineGetTypographicBounds(line, &ascent, &descent, &leading); 628 | CGFloat yMin = (CGFloat)floor(lineOrigin.y - descent); 629 | CGFloat yMax = (CGFloat)ceil(lineOrigin.y + ascent); 630 | 631 | // Check if we've already passed the line 632 | if (p.y > yMax) { 633 | break; 634 | } 635 | // Check if the point is within this line vertically 636 | if (p.y >= yMin) { 637 | // Check if the point is within this line horizontally 638 | if (p.x >= lineOrigin.x && p.x <= lineOrigin.x + width) { 639 | // Convert CT coordinates to line-relative coordinates 640 | CGPoint relativePoint = CGPointMake(p.x - lineOrigin.x, p.y - lineOrigin.y); 641 | idx = CTLineGetStringIndexForPosition(line, relativePoint); 642 | break; 643 | } 644 | } 645 | } 646 | 647 | CFRelease(frame); 648 | CFRelease(path); 649 | 650 | return idx; 651 | } 652 | 653 | - (void)drawFramesetter:(CTFramesetterRef)framesetter 654 | attributedString:(NSAttributedString *)attributedString 655 | textRange:(CFRange)textRange 656 | inRect:(CGRect)rect 657 | context:(CGContextRef)c 658 | { 659 | CGMutablePathRef path = CGPathCreateMutable(); 660 | CGPathAddRect(path, NULL, rect); 661 | CTFrameRef frame = CTFramesetterCreateFrame(framesetter, textRange, path, NULL); 662 | 663 | [self drawBackground:frame inRect:rect context:c]; 664 | 665 | CFArrayRef lines = CTFrameGetLines(frame); 666 | NSInteger numberOfLines = self.numberOfLines > 0 ? MIN(self.numberOfLines, CFArrayGetCount(lines)) : CFArrayGetCount(lines); 667 | BOOL truncateLastLine = (self.lineBreakMode == TTTLineBreakByTruncatingHead || self.lineBreakMode == TTTLineBreakByTruncatingMiddle || self.lineBreakMode == TTTLineBreakByTruncatingTail); 668 | 669 | CGPoint lineOrigins[numberOfLines]; 670 | CTFrameGetLineOrigins(frame, CFRangeMake(0, numberOfLines), lineOrigins); 671 | 672 | for (CFIndex lineIndex = 0; lineIndex < numberOfLines; lineIndex++) { 673 | CGPoint lineOrigin = lineOrigins[lineIndex]; 674 | CGContextSetTextPosition(c, lineOrigin.x, lineOrigin.y); 675 | CTLineRef line = CFArrayGetValueAtIndex(lines, lineIndex); 676 | 677 | CGFloat descent = 0.0f; 678 | CTLineGetTypographicBounds((CTLineRef)line, NULL, &descent, NULL); 679 | 680 | // Adjust pen offset for flush depending on text alignment 681 | CGFloat flushFactor = 0.0f; 682 | switch (self.textAlignment) { 683 | case TTTTextAlignmentCenter: 684 | flushFactor = 0.5f; 685 | break; 686 | case TTTTextAlignmentRight: 687 | flushFactor = 1.0f; 688 | break; 689 | case TTTTextAlignmentLeft: 690 | default: 691 | break; 692 | } 693 | 694 | if (lineIndex == numberOfLines - 1 && truncateLastLine) { 695 | // Check if the range of text in the last line reaches the end of the full attributed string 696 | CFRange lastLineRange = CTLineGetStringRange(line); 697 | 698 | if (!(lastLineRange.length == 0 && lastLineRange.location == 0) && lastLineRange.location + lastLineRange.length < textRange.location + textRange.length) { 699 | // Get correct truncationType and attribute position 700 | CTLineTruncationType truncationType; 701 | CFIndex truncationAttributePosition = lastLineRange.location; 702 | TTTLineBreakMode lineBreakMode = self.lineBreakMode; 703 | 704 | // Multiple lines, only use UILineBreakModeTailTruncation 705 | if (numberOfLines != 1) { 706 | lineBreakMode = TTTLineBreakByTruncatingTail; 707 | } 708 | 709 | switch (lineBreakMode) { 710 | case TTTLineBreakByTruncatingHead: 711 | truncationType = kCTLineTruncationStart; 712 | break; 713 | case TTTLineBreakByTruncatingMiddle: 714 | truncationType = kCTLineTruncationMiddle; 715 | truncationAttributePosition += (lastLineRange.length / 2); 716 | break; 717 | case TTTLineBreakByTruncatingTail: 718 | default: 719 | truncationType = kCTLineTruncationEnd; 720 | truncationAttributePosition += (lastLineRange.length - 1); 721 | break; 722 | } 723 | 724 | NSString *truncationTokenString = self.truncationTokenString; 725 | if (!truncationTokenString) { 726 | truncationTokenString = @"\u2026"; // Unicode Character 'HORIZONTAL ELLIPSIS' (U+2026) 727 | } 728 | 729 | NSDictionary *truncationTokenStringAttributes = self.truncationTokenStringAttributes; 730 | if (!truncationTokenStringAttributes) { 731 | truncationTokenStringAttributes = [attributedString attributesAtIndex:(NSUInteger)truncationAttributePosition effectiveRange:NULL]; 732 | } 733 | 734 | NSAttributedString *attributedTokenString = [[NSAttributedString alloc] initWithString:truncationTokenString attributes:truncationTokenStringAttributes]; 735 | CTLineRef truncationToken = CTLineCreateWithAttributedString((__bridge CFAttributedStringRef)attributedTokenString); 736 | 737 | // Append truncationToken to the string 738 | // because if string isn't too long, CT wont add the truncationToken on it's own 739 | // There is no change of a double truncationToken because CT only add the token if it removes characters (and the one we add will go first) 740 | NSMutableAttributedString *truncationString = [[attributedString attributedSubstringFromRange:NSMakeRange((NSUInteger)lastLineRange.location, (NSUInteger)lastLineRange.length)] mutableCopy]; 741 | if (lastLineRange.length > 0) { 742 | // Remove any newline at the end (we don't want newline space between the text and the truncation token). There can only be one, because the second would be on the next line. 743 | unichar lastCharacter = [[truncationString string] characterAtIndex:(NSUInteger)(lastLineRange.length - 1)]; 744 | if ([[NSCharacterSet newlineCharacterSet] characterIsMember:lastCharacter]) { 745 | [truncationString deleteCharactersInRange:NSMakeRange((NSUInteger)(lastLineRange.length - 1), 1)]; 746 | } 747 | } 748 | [truncationString appendAttributedString:attributedTokenString]; 749 | CTLineRef truncationLine = CTLineCreateWithAttributedString((__bridge CFAttributedStringRef)truncationString); 750 | 751 | // Truncate the line in case it is too long. 752 | CTLineRef truncatedLine = CTLineCreateTruncatedLine(truncationLine, rect.size.width, truncationType, truncationToken); 753 | if (!truncatedLine) { 754 | // If the line is not as wide as the truncationToken, truncatedLine is NULL 755 | truncatedLine = CFRetain(truncationToken); 756 | } 757 | 758 | CGFloat penOffset = (CGFloat)CTLineGetPenOffsetForFlush(truncatedLine, flushFactor, rect.size.width); 759 | CGContextSetTextPosition(c, penOffset, lineOrigin.y - descent - self.font.descender); 760 | 761 | CTLineDraw(truncatedLine, c); 762 | 763 | CFRelease(truncatedLine); 764 | CFRelease(truncationLine); 765 | CFRelease(truncationToken); 766 | } else { 767 | CGContextSetTextPosition(c, lineOrigin.x, lineOrigin.y - descent - self.font.descender); 768 | CTLineDraw(line, c); 769 | } 770 | } else { 771 | CGContextSetTextPosition(c, lineOrigin.x, lineOrigin.y - descent - self.font.descender); 772 | CTLineDraw(line, c); 773 | } 774 | } 775 | 776 | [self drawStrike:frame inRect:rect context:c]; 777 | 778 | CFRelease(frame); 779 | CFRelease(path); 780 | } 781 | 782 | - (void)drawBackground:(CTFrameRef)frame 783 | inRect:(CGRect)rect 784 | context:(CGContextRef)c 785 | { 786 | NSArray *lines = (__bridge NSArray *)CTFrameGetLines(frame); 787 | CGPoint origins[[lines count]]; 788 | CTFrameGetLineOrigins(frame, CFRangeMake(0, 0), origins); 789 | 790 | // Compensate for y-offset of text rect from vertical positioning 791 | CGFloat yOffset = self.textInsets.top - [self textRectForBounds:self.bounds limitedToNumberOfLines:self.numberOfLines].origin.y; 792 | 793 | CFIndex lineIndex = 0; 794 | for (id line in lines) { 795 | CGFloat ascent = 0.0f, descent = 0.0f, leading = 0.0f; 796 | CGFloat width = (CGFloat)CTLineGetTypographicBounds((__bridge CTLineRef)line, &ascent, &descent, &leading) ; 797 | CGRect lineBounds = CGRectMake(rect.origin.x, rect.origin.y, width, ascent + descent + leading) ; 798 | lineBounds.origin.x += origins[lineIndex].x; 799 | lineBounds.origin.y += origins[lineIndex].y; 800 | 801 | for (id glyphRun in (__bridge NSArray *)CTLineGetGlyphRuns((__bridge CTLineRef)line)) { 802 | NSDictionary *attributes = (__bridge NSDictionary *)CTRunGetAttributes((__bridge CTRunRef) glyphRun); 803 | CGColorRef strokeColor = (__bridge CGColorRef)[attributes objectForKey:kTTTBackgroundStrokeColorAttributeName]; 804 | CGColorRef fillColor = (__bridge CGColorRef)[attributes objectForKey:kTTTBackgroundFillColorAttributeName]; 805 | UIEdgeInsets fillPadding = [[attributes objectForKey:kTTTBackgroundFillPaddingAttributeName] UIEdgeInsetsValue]; 806 | CGFloat cornerRadius = [[attributes objectForKey:kTTTBackgroundCornerRadiusAttributeName] floatValue]; 807 | CGFloat lineWidth = [[attributes objectForKey:kTTTBackgroundLineWidthAttributeName] floatValue]; 808 | 809 | if (strokeColor || fillColor) { 810 | CGRect runBounds = CGRectZero; 811 | CGFloat runAscent = 0.0f; 812 | CGFloat runDescent = 0.0f; 813 | 814 | runBounds.size.width = (CGFloat)CTRunGetTypographicBounds((__bridge CTRunRef)glyphRun, CFRangeMake(0, 0), &runAscent, &runDescent, NULL) + fillPadding.left + fillPadding.right; 815 | runBounds.size.height = runAscent + runDescent + fillPadding.top + fillPadding.bottom; 816 | 817 | CGFloat xOffset = 0.0f; 818 | CFRange glyphRange = CTRunGetStringRange((__bridge CTRunRef)glyphRun); 819 | switch (CTRunGetStatus((__bridge CTRunRef)glyphRun)) { 820 | case kCTRunStatusRightToLeft: 821 | xOffset = CTLineGetOffsetForStringIndex((__bridge CTLineRef)line, glyphRange.location + glyphRange.length, NULL); 822 | break; 823 | default: 824 | xOffset = CTLineGetOffsetForStringIndex((__bridge CTLineRef)line, glyphRange.location, NULL); 825 | break; 826 | } 827 | 828 | runBounds.origin.x = origins[lineIndex].x + rect.origin.x + xOffset - fillPadding.left - rect.origin.x; 829 | runBounds.origin.y = origins[lineIndex].y + rect.origin.y + yOffset - fillPadding.bottom - rect.origin.y; 830 | runBounds.origin.y -= runDescent; 831 | 832 | // Don't draw higlightedLinkBackground too far to the right 833 | if (CGRectGetWidth(runBounds) > CGRectGetWidth(lineBounds)) { 834 | runBounds.size.width = CGRectGetWidth(lineBounds); 835 | } 836 | 837 | CGPathRef path = [[UIBezierPath bezierPathWithRoundedRect:CGRectInset(CGRectInset(runBounds, -1.0f, 0.0f), lineWidth, lineWidth) cornerRadius:cornerRadius] CGPath]; 838 | 839 | CGContextSetLineJoin(c, kCGLineJoinRound); 840 | 841 | if (fillColor) { 842 | CGContextSetFillColorWithColor(c, fillColor); 843 | CGContextAddPath(c, path); 844 | CGContextFillPath(c); 845 | } 846 | 847 | if (strokeColor) { 848 | CGContextSetStrokeColorWithColor(c, strokeColor); 849 | CGContextAddPath(c, path); 850 | CGContextStrokePath(c); 851 | } 852 | } 853 | } 854 | 855 | lineIndex++; 856 | } 857 | } 858 | 859 | - (void)drawStrike:(CTFrameRef)frame 860 | inRect:(__unused CGRect)rect 861 | context:(CGContextRef)c 862 | { 863 | NSArray *lines = (__bridge NSArray *)CTFrameGetLines(frame); 864 | CGPoint origins[[lines count]]; 865 | CTFrameGetLineOrigins(frame, CFRangeMake(0, 0), origins); 866 | 867 | CFIndex lineIndex = 0; 868 | for (id line in lines) { 869 | CGFloat ascent = 0.0f, descent = 0.0f, leading = 0.0f; 870 | CGFloat width = (CGFloat)CTLineGetTypographicBounds((__bridge CTLineRef)line, &ascent, &descent, &leading) ; 871 | CGRect lineBounds = CGRectMake(0.0f, 0.0f, width, ascent + descent + leading) ; 872 | lineBounds.origin.x = origins[lineIndex].x; 873 | lineBounds.origin.y = origins[lineIndex].y; 874 | 875 | for (id glyphRun in (__bridge NSArray *)CTLineGetGlyphRuns((__bridge CTLineRef)line)) { 876 | NSDictionary *attributes = (__bridge NSDictionary *)CTRunGetAttributes((__bridge CTRunRef) glyphRun); 877 | BOOL strikeOut = [[attributes objectForKey:kTTTStrikeOutAttributeName] boolValue]; 878 | NSInteger superscriptStyle = [[attributes objectForKey:(id)kCTSuperscriptAttributeName] integerValue]; 879 | 880 | if (strikeOut) { 881 | CGRect runBounds = CGRectZero; 882 | CGFloat runAscent = 0.0f; 883 | CGFloat runDescent = 0.0f; 884 | 885 | runBounds.size.width = (CGFloat)CTRunGetTypographicBounds((__bridge CTRunRef)glyphRun, CFRangeMake(0, 0), &runAscent, &runDescent, NULL); 886 | runBounds.size.height = runAscent + runDescent; 887 | 888 | CGFloat xOffset = 0.0f; 889 | CFRange glyphRange = CTRunGetStringRange((__bridge CTRunRef)glyphRun); 890 | switch (CTRunGetStatus((__bridge CTRunRef)glyphRun)) { 891 | case kCTRunStatusRightToLeft: 892 | xOffset = CTLineGetOffsetForStringIndex((__bridge CTLineRef)line, glyphRange.location + glyphRange.length, NULL); 893 | break; 894 | default: 895 | xOffset = CTLineGetOffsetForStringIndex((__bridge CTLineRef)line, glyphRange.location, NULL); 896 | break; 897 | } 898 | runBounds.origin.x = origins[lineIndex].x + xOffset; 899 | runBounds.origin.y = origins[lineIndex].y; 900 | runBounds.origin.y -= runDescent; 901 | 902 | // Don't draw strikeout too far to the right 903 | if (CGRectGetWidth(runBounds) > CGRectGetWidth(lineBounds)) { 904 | runBounds.size.width = CGRectGetWidth(lineBounds); 905 | } 906 | 907 | switch (superscriptStyle) { 908 | case 1: 909 | runBounds.origin.y -= runAscent * 0.47f; 910 | break; 911 | case -1: 912 | runBounds.origin.y += runAscent * 0.25f; 913 | break; 914 | default: 915 | break; 916 | } 917 | 918 | // Use text color, or default to black 919 | id color = [attributes objectForKey:(id)kCTForegroundColorAttributeName]; 920 | if (color) { 921 | if ([color isKindOfClass:[UIColor class]]) { 922 | CGContextSetStrokeColorWithColor(c, [color CGColor]); 923 | } else { 924 | CGContextSetStrokeColorWithColor(c, (__bridge CGColorRef)color); 925 | } 926 | } else { 927 | CGContextSetGrayStrokeColor(c, 0.0f, 1.0); 928 | } 929 | 930 | CTFontRef font = CTFontCreateWithName((__bridge CFStringRef)self.font.fontName, self.font.pointSize, NULL); 931 | CGContextSetLineWidth(c, CTFontGetUnderlineThickness(font)); 932 | CFRelease(font); 933 | 934 | CGFloat y = CGFloat_round(runBounds.origin.y + runBounds.size.height / 2.0f); 935 | CGContextMoveToPoint(c, runBounds.origin.x, y); 936 | CGContextAddLineToPoint(c, runBounds.origin.x + runBounds.size.width, y); 937 | 938 | CGContextStrokePath(c); 939 | } 940 | } 941 | 942 | lineIndex++; 943 | } 944 | } 945 | 946 | #pragma mark - TTTAttributedLabel 947 | 948 | - (void)setText:(id)text { 949 | NSParameterAssert(!text || [text isKindOfClass:[NSAttributedString class]] || [text isKindOfClass:[NSString class]]); 950 | 951 | if ([text isKindOfClass:[NSString class]]) { 952 | [self setText:text afterInheritingLabelAttributesAndConfiguringWithBlock:nil]; 953 | return; 954 | } 955 | 956 | self.attributedText = text; 957 | self.activeLink = nil; 958 | 959 | self.links = [NSArray array]; 960 | if (self.attributedText && self.enabledTextCheckingTypes) { 961 | #if __IPHONE_OS_VERSION_MIN_REQUIRED < 50000 962 | __unsafe_unretained __typeof(self)weakSelf = self; 963 | #else 964 | __weak __typeof(self)weakSelf = self; 965 | #endif 966 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 967 | __strong __typeof(weakSelf)strongSelf = weakSelf; 968 | 969 | NSDataDetector *dataDetector = strongSelf.dataDetector; 970 | if (dataDetector && [dataDetector respondsToSelector:@selector(matchesInString:options:range:)]) { 971 | NSArray *results = [dataDetector matchesInString:[(NSAttributedString *)text string] options:0 range:NSMakeRange(0, [(NSAttributedString *)text length])]; 972 | if ([results count] > 0) { 973 | dispatch_sync(dispatch_get_main_queue(), ^{ 974 | if ([[strongSelf.attributedText string] isEqualToString:[(NSAttributedString *)text string]]) { 975 | [strongSelf addLinksWithTextCheckingResults:results attributes:strongSelf.linkAttributes]; 976 | } 977 | }); 978 | } 979 | } 980 | }); 981 | } 982 | 983 | #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 984 | [self.attributedText enumerateAttribute:NSLinkAttributeName inRange:NSMakeRange(0, self.attributedText.length) options:0 usingBlock:^(id value, __unused NSRange range, __unused BOOL *stop) { 985 | if (value) { 986 | NSURL *URL = [value isKindOfClass:[NSString class]] ? [NSURL URLWithString:value] : value; 987 | [self addLinkToURL:URL withRange:range]; 988 | } 989 | }]; 990 | #endif 991 | 992 | [super setText:[self.attributedText string]]; 993 | } 994 | 995 | - (void)setText:(id)text 996 | afterInheritingLabelAttributesAndConfiguringWithBlock:(NSMutableAttributedString *(^)(NSMutableAttributedString *mutableAttributedString))block 997 | { 998 | NSMutableAttributedString *mutableAttributedString = nil; 999 | if ([text isKindOfClass:[NSString class]]) { 1000 | mutableAttributedString = [[NSMutableAttributedString alloc] initWithString:text attributes:NSAttributedStringAttributesFromLabel(self)]; 1001 | } else { 1002 | mutableAttributedString = [[NSMutableAttributedString alloc] initWithAttributedString:text]; 1003 | [mutableAttributedString addAttributes:NSAttributedStringAttributesFromLabel(self) range:NSMakeRange(0, [mutableAttributedString length])]; 1004 | } 1005 | 1006 | if (block) { 1007 | mutableAttributedString = block(mutableAttributedString); 1008 | } 1009 | 1010 | [self setText:mutableAttributedString]; 1011 | } 1012 | 1013 | - (void)setActiveLink:(NSTextCheckingResult *)activeLink { 1014 | _activeLink = activeLink; 1015 | 1016 | if (_activeLink && [self.activeLinkAttributes count] > 0) { 1017 | if (!self.inactiveAttributedText) { 1018 | self.inactiveAttributedText = [self.attributedText copy]; 1019 | } 1020 | 1021 | NSMutableAttributedString *mutableAttributedString = [self.inactiveAttributedText mutableCopy]; 1022 | if (self.activeLink.range.length > 0 && NSLocationInRange(NSMaxRange(self.activeLink.range) - 1, NSMakeRange(0, [self.inactiveAttributedText length]))) { 1023 | [mutableAttributedString addAttributes:self.activeLinkAttributes range:self.activeLink.range]; 1024 | } 1025 | 1026 | self.attributedText = mutableAttributedString; 1027 | [self setNeedsDisplay]; 1028 | 1029 | [CATransaction flush]; 1030 | } else if (self.inactiveAttributedText) { 1031 | self.attributedText = self.inactiveAttributedText; 1032 | self.inactiveAttributedText = nil; 1033 | 1034 | [self setNeedsDisplay]; 1035 | } 1036 | } 1037 | 1038 | #pragma mark - UILabel 1039 | 1040 | - (void)setHighlighted:(BOOL)highlighted { 1041 | [super setHighlighted:highlighted]; 1042 | [self setNeedsDisplay]; 1043 | } 1044 | 1045 | // Fixes crash when loading from a UIStoryboard 1046 | - (UIColor *)textColor { 1047 | UIColor *color = [super textColor]; 1048 | if (!color) { 1049 | color = [UIColor blackColor]; 1050 | } 1051 | 1052 | return color; 1053 | } 1054 | 1055 | - (void)setTextColor:(UIColor *)textColor { 1056 | UIColor *oldTextColor = self.textColor; 1057 | [super setTextColor:textColor]; 1058 | 1059 | // Redraw to allow any ColorFromContext attributes a chance to update 1060 | if (textColor != oldTextColor) { 1061 | [self setNeedsFramesetter]; 1062 | [self setNeedsDisplay]; 1063 | } 1064 | } 1065 | 1066 | - (CGRect)textRectForBounds:(CGRect)bounds 1067 | limitedToNumberOfLines:(NSInteger)numberOfLines 1068 | { 1069 | bounds = UIEdgeInsetsInsetRect(bounds, self.textInsets); 1070 | if (!self.attributedText) { 1071 | return [super textRectForBounds:bounds limitedToNumberOfLines:numberOfLines]; 1072 | } 1073 | 1074 | CGRect textRect = bounds; 1075 | 1076 | // Calculate height with a minimum of double the font pointSize, to ensure that CTFramesetterSuggestFrameSizeWithConstraints doesn't return CGSizeZero, as it would if textRect height is insufficient. 1077 | textRect.size.height = MAX(self.font.pointSize * 2.0f, bounds.size.height); 1078 | 1079 | // Adjust the text to be in the center vertically, if the text size is smaller than bounds 1080 | CGSize textSize = CTFramesetterSuggestFrameSizeWithConstraints([self framesetter], CFRangeMake(0, (CFIndex)[self.attributedText length]), NULL, textRect.size, NULL); 1081 | textSize = CGSizeMake(CGFloat_ceil(textSize.width), CGFloat_ceil(textSize.height)); // Fix for iOS 4, CTFramesetterSuggestFrameSizeWithConstraints sometimes returns fractional sizes 1082 | 1083 | if (textSize.height < textRect.size.height) { 1084 | CGFloat yOffset = 0.0f; 1085 | switch (self.verticalAlignment) { 1086 | case TTTAttributedLabelVerticalAlignmentCenter: 1087 | yOffset = CGFloat_floor((bounds.size.height - textSize.height) / 2.0f); 1088 | break; 1089 | case TTTAttributedLabelVerticalAlignmentBottom: 1090 | yOffset = bounds.size.height - textSize.height; 1091 | break; 1092 | case TTTAttributedLabelVerticalAlignmentTop: 1093 | default: 1094 | break; 1095 | } 1096 | 1097 | textRect.origin.y += yOffset; 1098 | } 1099 | 1100 | return textRect; 1101 | } 1102 | 1103 | - (void)drawTextInRect:(CGRect)rect { 1104 | CGRect insetRect = UIEdgeInsetsInsetRect(rect, self.textInsets); 1105 | if (!self.attributedText) { 1106 | [super drawTextInRect:insetRect]; 1107 | return; 1108 | } 1109 | 1110 | NSAttributedString *originalAttributedText = nil; 1111 | 1112 | // Adjust the font size to fit width, if necessarry 1113 | if (self.adjustsFontSizeToFitWidth && self.numberOfLines > 0) { 1114 | // Use infinite width to find the max width, which will be compared to availableWidth if needed. 1115 | CGSize maxSize = (self.numberOfLines > 1) ? CGSizeMake(TTTFLOAT_MAX, TTTFLOAT_MAX) : CGSizeZero; 1116 | 1117 | CGFloat textWidth = [self sizeThatFits:maxSize].width; 1118 | CGFloat availableWidth = self.frame.size.width * self.numberOfLines; 1119 | if (self.numberOfLines > 1 && self.lineBreakMode == TTTLineBreakByWordWrapping) { 1120 | textWidth *= kTTTLineBreakWordWrapTextWidthScalingFactor; 1121 | } 1122 | 1123 | if (textWidth > availableWidth && textWidth > 0.0f) { 1124 | originalAttributedText = [self.attributedText copy]; 1125 | self.attributedText = NSAttributedStringByScalingFontSize(self.attributedText, availableWidth / textWidth); 1126 | } 1127 | } 1128 | 1129 | CGContextRef c = UIGraphicsGetCurrentContext(); 1130 | CGContextSaveGState(c); 1131 | { 1132 | CGContextSetTextMatrix(c, CGAffineTransformIdentity); 1133 | 1134 | // Inverts the CTM to match iOS coordinates (otherwise text draws upside-down; Mac OS's system is different) 1135 | CGContextTranslateCTM(c, 0.0f, insetRect.size.height); 1136 | CGContextScaleCTM(c, 1.0f, -1.0f); 1137 | 1138 | CFRange textRange = CFRangeMake(0, (CFIndex)[self.attributedText length]); 1139 | 1140 | // First, get the text rect (which takes vertical centering into account) 1141 | CGRect textRect = [self textRectForBounds:rect limitedToNumberOfLines:self.numberOfLines]; 1142 | 1143 | // CoreText draws it's text aligned to the bottom, so we move the CTM here to take our vertical offsets into account 1144 | CGContextTranslateCTM(c, insetRect.origin.x, insetRect.size.height - textRect.origin.y - textRect.size.height); 1145 | 1146 | // Second, trace the shadow before the actual text, if we have one 1147 | if (self.shadowColor && !self.highlighted) { 1148 | CGContextSetShadowWithColor(c, self.shadowOffset, self.shadowRadius, [self.shadowColor CGColor]); 1149 | } else if (self.highlightedShadowColor) { 1150 | CGContextSetShadowWithColor(c, self.highlightedShadowOffset, self.highlightedShadowRadius, [self.highlightedShadowColor CGColor]); 1151 | } 1152 | 1153 | // Finally, draw the text or highlighted text itself (on top of the shadow, if there is one) 1154 | if (self.highlightedTextColor && self.highlighted) { 1155 | NSMutableAttributedString *highlightAttributedString = [self.renderedAttributedText mutableCopy]; 1156 | [highlightAttributedString addAttribute:(__bridge NSString *)kCTForegroundColorAttributeName value:(id)[self.highlightedTextColor CGColor] range:NSMakeRange(0, highlightAttributedString.length)]; 1157 | 1158 | if (![self highlightFramesetter]) { 1159 | CTFramesetterRef highlightFramesetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)highlightAttributedString); 1160 | [self setHighlightFramesetter:highlightFramesetter]; 1161 | CFRelease(highlightFramesetter); 1162 | } 1163 | 1164 | [self drawFramesetter:[self highlightFramesetter] attributedString:highlightAttributedString textRange:textRange inRect:textRect context:c]; 1165 | } else { 1166 | [self drawFramesetter:[self framesetter] attributedString:self.renderedAttributedText textRange:textRange inRect:textRect context:c]; 1167 | } 1168 | 1169 | // If we adjusted the font size, set it back to its original size 1170 | if (originalAttributedText) { 1171 | // Use ivar directly to avoid clearing out framesetter and renderedAttributedText 1172 | _attributedText = originalAttributedText; 1173 | } 1174 | } 1175 | CGContextRestoreGState(c); 1176 | } 1177 | 1178 | #pragma mark - UIView 1179 | 1180 | - (CGSize)sizeThatFits:(CGSize)size { 1181 | if (!self.attributedText) { 1182 | return [super sizeThatFits:size]; 1183 | } else { 1184 | size = CTFramesetterSuggestFrameSizeForAttributedStringWithConstraints([self framesetter], self.attributedText, size, (NSUInteger)self.numberOfLines); 1185 | size.width += self.textInsets.left + self.textInsets.right; 1186 | size.height += self.textInsets.top + self.textInsets.bottom; 1187 | 1188 | return size; 1189 | } 1190 | } 1191 | 1192 | - (CGSize)intrinsicContentSize { 1193 | // There's an implicit width from the original UILabel implementation 1194 | return [self sizeThatFits:[super intrinsicContentSize]]; 1195 | } 1196 | 1197 | #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 1198 | - (void)tintColorDidChange { 1199 | BOOL isInactive = (self.tintAdjustmentMode == UIViewTintAdjustmentModeDimmed); 1200 | 1201 | NSDictionary *attributesToRemove = isInactive ? self.linkAttributes : self.inactiveLinkAttributes; 1202 | NSDictionary *attributesToAdd = isInactive ? self.inactiveLinkAttributes : self.linkAttributes; 1203 | 1204 | NSMutableAttributedString *mutableAttributedString = [self.attributedText mutableCopy]; 1205 | for (NSTextCheckingResult *result in self.links) { 1206 | [attributesToRemove enumerateKeysAndObjectsUsingBlock:^(NSString *name, __unused id value, __unused BOOL *stop) { 1207 | [mutableAttributedString removeAttribute:name range:result.range]; 1208 | }]; 1209 | 1210 | if (attributesToAdd) { 1211 | [mutableAttributedString addAttributes:attributesToAdd range:result.range]; 1212 | } 1213 | } 1214 | 1215 | self.attributedText = mutableAttributedString; 1216 | [self setNeedsDisplay]; 1217 | } 1218 | #endif 1219 | 1220 | - (UIView *)hitTest:(CGPoint)point 1221 | withEvent:(__unused UIEvent *)event 1222 | { 1223 | if (![self linkAtPoint:point]) { 1224 | return nil; 1225 | } 1226 | 1227 | return self; 1228 | } 1229 | 1230 | #pragma mark - UIResponder 1231 | 1232 | - (BOOL)canBecomeFirstResponder { 1233 | return YES; 1234 | } 1235 | 1236 | - (BOOL)canPerformAction:(SEL)action 1237 | withSender:(__unused id)sender 1238 | { 1239 | return (action == @selector(copy:)); 1240 | } 1241 | 1242 | - (void)touchesBegan:(NSSet *)touches 1243 | withEvent:(UIEvent *)event 1244 | { 1245 | UITouch *touch = [touches anyObject]; 1246 | 1247 | self.activeLink = [self linkAtPoint:[touch locationInView:self]]; 1248 | 1249 | if (!self.activeLink) { 1250 | [super touchesBegan:touches withEvent:event]; 1251 | } 1252 | } 1253 | 1254 | - (void)touchesMoved:(NSSet *)touches 1255 | withEvent:(UIEvent *)event 1256 | { 1257 | if (self.activeLink) { 1258 | UITouch *touch = [touches anyObject]; 1259 | 1260 | if (self.activeLink != [self linkAtPoint:[touch locationInView:self]]) { 1261 | self.activeLink = nil; 1262 | } 1263 | } else { 1264 | [super touchesMoved:touches withEvent:event]; 1265 | } 1266 | } 1267 | 1268 | - (void)touchesEnded:(NSSet *)touches 1269 | withEvent:(UIEvent *)event 1270 | { 1271 | if (self.activeLink) { 1272 | NSTextCheckingResult *result = self.activeLink; 1273 | self.activeLink = nil; 1274 | 1275 | switch (result.resultType) { 1276 | case NSTextCheckingTypeLink: 1277 | if ([self.delegate respondsToSelector:@selector(attributedLabel:didSelectLinkWithURL:)]) { 1278 | [self.delegate attributedLabel:self didSelectLinkWithURL:result.URL]; 1279 | return; 1280 | } 1281 | break; 1282 | case NSTextCheckingTypeAddress: 1283 | if ([self.delegate respondsToSelector:@selector(attributedLabel:didSelectLinkWithAddress:)]) { 1284 | [self.delegate attributedLabel:self didSelectLinkWithAddress:result.addressComponents]; 1285 | return; 1286 | } 1287 | break; 1288 | case NSTextCheckingTypePhoneNumber: 1289 | if ([self.delegate respondsToSelector:@selector(attributedLabel:didSelectLinkWithPhoneNumber:)]) { 1290 | [self.delegate attributedLabel:self didSelectLinkWithPhoneNumber:result.phoneNumber]; 1291 | return; 1292 | } 1293 | break; 1294 | case NSTextCheckingTypeDate: 1295 | if (result.timeZone && [self.delegate respondsToSelector:@selector(attributedLabel:didSelectLinkWithDate:timeZone:duration:)]) { 1296 | [self.delegate attributedLabel:self didSelectLinkWithDate:result.date timeZone:result.timeZone duration:result.duration]; 1297 | return; 1298 | } else if ([self.delegate respondsToSelector:@selector(attributedLabel:didSelectLinkWithDate:)]) { 1299 | [self.delegate attributedLabel:self didSelectLinkWithDate:result.date]; 1300 | return; 1301 | } 1302 | break; 1303 | case NSTextCheckingTypeTransitInformation: 1304 | if ([self.delegate respondsToSelector:@selector(attributedLabel:didSelectLinkWithTransitInformation:)]) { 1305 | [self.delegate attributedLabel:self didSelectLinkWithTransitInformation:result.components]; 1306 | return; 1307 | } 1308 | default: 1309 | break; 1310 | } 1311 | 1312 | // Fallback to `attributedLabel:didSelectLinkWithTextCheckingResult:` if no other delegate method matched. 1313 | if ([self.delegate respondsToSelector:@selector(attributedLabel:didSelectLinkWithTextCheckingResult:)]) { 1314 | [self.delegate attributedLabel:self didSelectLinkWithTextCheckingResult:result]; 1315 | } 1316 | } else { 1317 | [super touchesEnded:touches withEvent:event]; 1318 | } 1319 | } 1320 | 1321 | 1322 | - (void)touchesCancelled:(NSSet *)touches 1323 | withEvent:(UIEvent *)event 1324 | { 1325 | if (self.activeLink) { 1326 | self.activeLink = nil; 1327 | } else { 1328 | [super touchesCancelled:touches withEvent:event]; 1329 | } 1330 | } 1331 | 1332 | #pragma mark - UIResponderStandardEditActions 1333 | 1334 | - (void)copy:(__unused id)sender { 1335 | [[UIPasteboard generalPasteboard] setString:self.text]; 1336 | } 1337 | 1338 | #pragma mark - NSCoding 1339 | 1340 | - (void)encodeWithCoder:(NSCoder *)coder { 1341 | [super encodeWithCoder:coder]; 1342 | 1343 | [coder encodeObject:@(self.enabledTextCheckingTypes) forKey:NSStringFromSelector(@selector(enabledTextCheckingTypes))]; 1344 | 1345 | [coder encodeObject:self.links forKey:NSStringFromSelector(@selector(links))]; 1346 | if ([NSMutableParagraphStyle class]) { 1347 | [coder encodeObject:self.linkAttributes forKey:NSStringFromSelector(@selector(linkAttributes))]; 1348 | [coder encodeObject:self.activeLinkAttributes forKey:NSStringFromSelector(@selector(activeLinkAttributes))]; 1349 | [coder encodeObject:self.inactiveLinkAttributes forKey:NSStringFromSelector(@selector(inactiveLinkAttributes))]; 1350 | } 1351 | [coder encodeObject:@(self.shadowRadius) forKey:NSStringFromSelector(@selector(shadowRadius))]; 1352 | [coder encodeObject:@(self.highlightedShadowRadius) forKey:NSStringFromSelector(@selector(highlightedShadowRadius))]; 1353 | [coder encodeCGSize:self.highlightedShadowOffset forKey:NSStringFromSelector(@selector(highlightedShadowOffset))]; 1354 | [coder encodeObject:self.highlightedShadowColor forKey:NSStringFromSelector(@selector(highlightedShadowColor))]; 1355 | [coder encodeObject:@(self.kern) forKey:NSStringFromSelector(@selector(kern))]; 1356 | [coder encodeObject:@(self.firstLineIndent) forKey:NSStringFromSelector(@selector(firstLineIndent))]; 1357 | [coder encodeObject:@(self.leading) forKey:NSStringFromSelector(@selector(leading))]; 1358 | [coder encodeObject:@(self.lineHeightMultiple) forKey:NSStringFromSelector(@selector(lineHeightMultiple))]; 1359 | [coder encodeUIEdgeInsets:self.textInsets forKey:NSStringFromSelector(@selector(textInsets))]; 1360 | [coder encodeInteger:self.verticalAlignment forKey:NSStringFromSelector(@selector(verticalAlignment))]; 1361 | [coder encodeObject:self.truncationTokenString forKey:NSStringFromSelector(@selector(truncationTokenString))]; 1362 | [coder encodeObject:self.attributedText forKey:NSStringFromSelector(@selector(attributedText))]; 1363 | [coder encodeObject:self.text forKey:NSStringFromSelector(@selector(text))]; 1364 | } 1365 | 1366 | - (id)initWithCoder:(NSCoder *)coder { 1367 | self = [super initWithCoder:coder]; 1368 | if (!self) { 1369 | return nil; 1370 | } 1371 | 1372 | [self commonInit]; 1373 | 1374 | if ([coder containsValueForKey:NSStringFromSelector(@selector(enabledTextCheckingTypes))]) { 1375 | self.enabledTextCheckingTypes = [[coder decodeObjectForKey:NSStringFromSelector(@selector(enabledTextCheckingTypes))] unsignedLongLongValue]; 1376 | } 1377 | 1378 | if ([coder containsValueForKey:NSStringFromSelector(@selector(links))]) { 1379 | self.links = [coder decodeObjectForKey:NSStringFromSelector(@selector(links))]; 1380 | } 1381 | 1382 | if ([NSMutableParagraphStyle class]) { 1383 | if ([coder containsValueForKey:NSStringFromSelector(@selector(linkAttributes))]) { 1384 | self.linkAttributes = [coder decodeObjectForKey:NSStringFromSelector(@selector(linkAttributes))]; 1385 | } 1386 | 1387 | if ([coder containsValueForKey:NSStringFromSelector(@selector(activeLinkAttributes))]) { 1388 | self.activeLinkAttributes = [coder decodeObjectForKey:NSStringFromSelector(@selector(activeLinkAttributes))]; 1389 | } 1390 | 1391 | if ([coder containsValueForKey:NSStringFromSelector(@selector(inactiveLinkAttributes))]) { 1392 | self.inactiveLinkAttributes = [coder decodeObjectForKey:NSStringFromSelector(@selector(inactiveLinkAttributes))]; 1393 | } 1394 | } 1395 | 1396 | if ([coder containsValueForKey:NSStringFromSelector(@selector(shadowRadius))]) { 1397 | self.shadowRadius = [[coder decodeObjectForKey:NSStringFromSelector(@selector(shadowRadius))] floatValue]; 1398 | } 1399 | 1400 | if ([coder containsValueForKey:NSStringFromSelector(@selector(highlightedShadowRadius))]) { 1401 | self.highlightedShadowRadius = [[coder decodeObjectForKey:NSStringFromSelector(@selector(highlightedShadowRadius))] floatValue]; 1402 | } 1403 | 1404 | if ([coder containsValueForKey:NSStringFromSelector(@selector(highlightedShadowOffset))]) { 1405 | self.highlightedShadowOffset = [coder decodeCGSizeForKey:NSStringFromSelector(@selector(highlightedShadowOffset))]; 1406 | } 1407 | 1408 | if ([coder containsValueForKey:NSStringFromSelector(@selector(highlightedShadowColor))]) { 1409 | self.highlightedShadowColor = [coder decodeObjectForKey:NSStringFromSelector(@selector(highlightedShadowColor))]; 1410 | } 1411 | 1412 | if ([coder containsValueForKey:NSStringFromSelector(@selector(kern))]) { 1413 | self.kern = [[coder decodeObjectForKey:NSStringFromSelector(@selector(kern))] floatValue]; 1414 | } 1415 | 1416 | if ([coder containsValueForKey:NSStringFromSelector(@selector(firstLineIndent))]) { 1417 | self.firstLineIndent = [[coder decodeObjectForKey:NSStringFromSelector(@selector(firstLineIndent))] floatValue]; 1418 | } 1419 | 1420 | if ([coder containsValueForKey:NSStringFromSelector(@selector(leading))]) { 1421 | self.leading = [[coder decodeObjectForKey:NSStringFromSelector(@selector(leading))] floatValue]; 1422 | } 1423 | 1424 | if ([coder containsValueForKey:NSStringFromSelector(@selector(minimumLineHeight))]) { 1425 | self.minimumLineHeight = [[coder decodeObjectForKey:NSStringFromSelector(@selector(minimumLineHeight))] floatValue]; 1426 | } 1427 | 1428 | if ([coder containsValueForKey:NSStringFromSelector(@selector(maximumLineHeight))]) { 1429 | self.maximumLineHeight = [[coder decodeObjectForKey:NSStringFromSelector(@selector(maximumLineHeight))] floatValue]; 1430 | } 1431 | 1432 | if ([coder containsValueForKey:NSStringFromSelector(@selector(lineHeightMultiple))]) { 1433 | self.lineHeightMultiple = [[coder decodeObjectForKey:NSStringFromSelector(@selector(lineHeightMultiple))] floatValue]; 1434 | } 1435 | 1436 | if ([coder containsValueForKey:NSStringFromSelector(@selector(textInsets))]) { 1437 | self.textInsets = [coder decodeUIEdgeInsetsForKey:NSStringFromSelector(@selector(textInsets))]; 1438 | } 1439 | 1440 | if ([coder containsValueForKey:NSStringFromSelector(@selector(verticalAlignment))]) { 1441 | self.verticalAlignment = [coder decodeIntegerForKey:NSStringFromSelector(@selector(verticalAlignment))]; 1442 | } 1443 | 1444 | if ([coder containsValueForKey:NSStringFromSelector(@selector(truncationTokenString))]) { 1445 | self.truncationTokenString = [coder decodeObjectForKey:NSStringFromSelector(@selector(truncationTokenString))]; 1446 | } 1447 | 1448 | if ([coder containsValueForKey:NSStringFromSelector(@selector(attributedText))]) { 1449 | self.attributedText = [coder decodeObjectForKey:NSStringFromSelector(@selector(attributedText))]; 1450 | } else { 1451 | self.text = super.text; 1452 | } 1453 | 1454 | return self; 1455 | } 1456 | 1457 | @end 1458 | --------------------------------------------------------------------------------