├── .gitignore ├── .travis.yml ├── Example ├── AttributedStringDemo.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── WorkspaceSettings.xcsettings │ └── xcshareddata │ │ └── xcschemes │ │ └── AttributedStringDemo.xcscheme ├── AttributedStringDemo.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── WorkspaceSettings.xcsettings ├── AttributedStringDemo │ ├── ASDAppDelegate.h │ ├── ASDAppDelegate.m │ ├── ASDViewController.h │ ├── ASDViewController.m │ ├── ASDViewController.xib │ ├── AttributedStringDemo-Info.plist │ ├── AttributedStringDemo-Prefix.pch │ ├── Images.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── LaunchImage.launchimage │ │ │ └── Contents.json │ └── main.m ├── Podfile ├── Podfile.lock ├── Pods │ ├── Headers │ │ ├── Private │ │ │ └── OHAttributedStringAdditions │ │ │ │ ├── NSAttributedString+OHAdditions.h │ │ │ │ ├── NSMutableAttributedString+OHAdditions.h │ │ │ │ ├── OHAttributedStringAdditions.h │ │ │ │ ├── UIFont+OHAdditions.h │ │ │ │ └── UILabel+OHAdditions.h │ │ └── Public │ │ │ └── OHAttributedStringAdditions │ │ │ ├── NSAttributedString+OHAdditions.h │ │ │ ├── NSMutableAttributedString+OHAdditions.h │ │ │ ├── OHAttributedStringAdditions.h │ │ │ ├── UIFont+OHAdditions.h │ │ │ └── UILabel+OHAdditions.h │ ├── Local Podspecs │ │ └── OHAttributedStringAdditions.podspec.json │ ├── Manifest.lock │ ├── Pods.xcodeproj │ │ └── project.pbxproj │ └── Target Support Files │ │ ├── Pods-OHAttributedStringAdditions │ │ ├── Pods-OHAttributedStringAdditions-Private.xcconfig │ │ ├── Pods-OHAttributedStringAdditions-dummy.m │ │ ├── Pods-OHAttributedStringAdditions-prefix.pch │ │ └── Pods-OHAttributedStringAdditions.xcconfig │ │ └── Pods │ │ ├── Pods-acknowledgements.markdown │ │ ├── Pods-acknowledgements.plist │ │ ├── Pods-dummy.m │ │ ├── Pods-environment.h │ │ ├── Pods-resources.sh │ │ ├── Pods.debug.xcconfig │ │ └── Pods.release.xcconfig └── UnitTests │ ├── NSAttributedStringTests.m │ ├── NSMutableAttributedStringTests.m │ ├── OHASATestHelper.h │ ├── OHASATestHelper.m │ ├── UIFontTests.m │ └── UnitTests-Info.plist ├── LICENSE ├── OHAttributedStringAdditions.podspec ├── README.md ├── README.png └── Source ├── NSAttributedString+OHAdditions.h ├── NSAttributedString+OHAdditions.m ├── NSMutableAttributedString+OHAdditions.h ├── NSMutableAttributedString+OHAdditions.m ├── OHAttributedStringAdditions.h ├── UIFont+OHAdditions.h ├── UIFont+OHAdditions.m ├── UILabel+OHAdditions.h └── UILabel+OHAdditions.m /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.lock 3 | 4 | # Xcode 5 | # 6 | build/ 7 | *.pbxuser 8 | !default.pbxuser 9 | *.mode1v3 10 | !default.mode1v3 11 | *.mode2v3 12 | !default.mode2v3 13 | *.perspectivev3 14 | !default.perspectivev3 15 | xcuserdata 16 | *.xccheckout 17 | *.moved-aside 18 | DerivedData 19 | *.hmap 20 | *.ipa 21 | *.xcuserstate 22 | 23 | # CocoaPods 24 | !Podfile.lock 25 | !Manifest.lock 26 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | 3 | before_install: 4 | - gem install xcpretty --no-rdoc --no-ri --no-document --quiet 5 | 6 | script: 7 | - xcodebuild -workspace Example/AttributedStringDemo.xcworkspace -scheme AttributedStringDemo -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 5S,OS=latest' -configuration Release test | xcpretty -c ; exit ${PIPESTATUS[0]} 8 | 9 | -------------------------------------------------------------------------------- /Example/AttributedStringDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 05DB335C13C16470804021DF /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1104688BD199A34CE48E58F4 /* libPods.a */; }; 11 | 091FEAE4199C11A600505B79 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 091FEAE3199C11A600505B79 /* Foundation.framework */; }; 12 | 091FEAE6199C11A600505B79 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 091FEAE5199C11A600505B79 /* CoreGraphics.framework */; }; 13 | 091FEAE8199C11A600505B79 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 091FEAE7199C11A600505B79 /* UIKit.framework */; }; 14 | 091FEAF0199C11A600505B79 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 091FEAEF199C11A600505B79 /* main.m */; }; 15 | 091FEAF4199C11A600505B79 /* ASDAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 091FEAF3199C11A600505B79 /* ASDAppDelegate.m */; }; 16 | 091FEAFA199C11A600505B79 /* ASDViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 091FEAF9199C11A600505B79 /* ASDViewController.m */; }; 17 | 091FEAFC199C11A600505B79 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 091FEAFB199C11A600505B79 /* Images.xcassets */; }; 18 | 091FEB03199C11A600505B79 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 091FEB02199C11A600505B79 /* XCTest.framework */; }; 19 | 091FEB04199C11A600505B79 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 091FEAE3199C11A600505B79 /* Foundation.framework */; }; 20 | 091FEB05199C11A600505B79 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 091FEAE7199C11A600505B79 /* UIKit.framework */; }; 21 | 091FEB0F199C11A600505B79 /* UIFontTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 091FEB0E199C11A600505B79 /* UIFontTests.m */; }; 22 | 091FEB1A199C135100505B79 /* ASDViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 091FEB19199C135100505B79 /* ASDViewController.xib */; }; 23 | 094443F7199FE3CF00324F4A /* NSAttributedStringTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 094443F6199FE3CF00324F4A /* NSAttributedStringTests.m */; }; 24 | 094443F919A00D0000324F4A /* NSMutableAttributedStringTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 094443F819A00D0000324F4A /* NSMutableAttributedStringTests.m */; }; 25 | 09A971EC19BCB61300F82C83 /* OHASATestHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 09A971EA19BCB4DA00F82C83 /* OHASATestHelper.m */; }; 26 | 6FEDFA659CBB92234B74D1B0 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1104688BD199A34CE48E58F4 /* libPods.a */; }; 27 | /* End PBXBuildFile section */ 28 | 29 | /* Begin PBXContainerItemProxy section */ 30 | 091FEB06199C11A600505B79 /* PBXContainerItemProxy */ = { 31 | isa = PBXContainerItemProxy; 32 | containerPortal = 091FEAD8199C11A600505B79 /* Project object */; 33 | proxyType = 1; 34 | remoteGlobalIDString = 091FEADF199C11A600505B79; 35 | remoteInfo = AttributedStringDemo; 36 | }; 37 | /* End PBXContainerItemProxy section */ 38 | 39 | /* Begin PBXFileReference section */ 40 | 091DC22A1A3C90DF009A6103 /* Pods.debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = ""; }; 41 | 091DC22B1A3C90DF009A6103 /* Pods.release.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = ""; }; 42 | 091FEAE0199C11A600505B79 /* AttributedStringDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AttributedStringDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 43 | 091FEAE3199C11A600505B79 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 44 | 091FEAE5199C11A600505B79 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 45 | 091FEAE7199C11A600505B79 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 46 | 091FEAEB199C11A600505B79 /* AttributedStringDemo-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "AttributedStringDemo-Info.plist"; sourceTree = ""; }; 47 | 091FEAEF199C11A600505B79 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 48 | 091FEAF1199C11A600505B79 /* AttributedStringDemo-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "AttributedStringDemo-Prefix.pch"; sourceTree = ""; }; 49 | 091FEAF2199C11A600505B79 /* ASDAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ASDAppDelegate.h; sourceTree = ""; }; 50 | 091FEAF3199C11A600505B79 /* ASDAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ASDAppDelegate.m; sourceTree = ""; }; 51 | 091FEAF8199C11A600505B79 /* ASDViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ASDViewController.h; sourceTree = ""; }; 52 | 091FEAF9199C11A600505B79 /* ASDViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ASDViewController.m; sourceTree = ""; }; 53 | 091FEAFB199C11A600505B79 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 54 | 091FEB01199C11A600505B79 /* UnitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = UnitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 55 | 091FEB02199C11A600505B79 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 56 | 091FEB0A199C11A600505B79 /* UnitTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "UnitTests-Info.plist"; sourceTree = ""; }; 57 | 091FEB0E199C11A600505B79 /* UIFontTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = UIFontTests.m; sourceTree = ""; }; 58 | 091FEB18199C12B300505B79 /* OHAttributedStringAdditions.podspec */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; name = OHAttributedStringAdditions.podspec; path = ../OHAttributedStringAdditions.podspec; sourceTree = ""; }; 59 | 091FEB19199C135100505B79 /* ASDViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ASDViewController.xib; sourceTree = ""; }; 60 | 0925DAC0199C5AE6001D9F8B /* README.md */ = {isa = PBXFileReference; lastKnownFileType = text; name = README.md; path = ../README.md; sourceTree = ""; }; 61 | 094443F6199FE3CF00324F4A /* NSAttributedStringTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = NSAttributedStringTests.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; 62 | 094443F819A00D0000324F4A /* NSMutableAttributedStringTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = NSMutableAttributedStringTests.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; 63 | 09A971E919BCB4DA00F82C83 /* OHASATestHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OHASATestHelper.h; sourceTree = ""; }; 64 | 09A971EA19BCB4DA00F82C83 /* OHASATestHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OHASATestHelper.m; sourceTree = ""; }; 65 | 0C4F6E012205CD8A335CB820 /* libPods-UnitTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-UnitTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 66 | 1104688BD199A34CE48E58F4 /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; }; 67 | 48842986E011607035CF900E /* libPods-AttributedStringDemo.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-AttributedStringDemo.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 68 | 6FF2008EC8614D5D8BD46C84 /* libPods-AttributedStringDemoTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-AttributedStringDemoTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 69 | /* End PBXFileReference section */ 70 | 71 | /* Begin PBXFrameworksBuildPhase section */ 72 | 091FEADD199C11A600505B79 /* Frameworks */ = { 73 | isa = PBXFrameworksBuildPhase; 74 | buildActionMask = 2147483647; 75 | files = ( 76 | 091FEAE6199C11A600505B79 /* CoreGraphics.framework in Frameworks */, 77 | 091FEAE8199C11A600505B79 /* UIKit.framework in Frameworks */, 78 | 091FEAE4199C11A600505B79 /* Foundation.framework in Frameworks */, 79 | 6FEDFA659CBB92234B74D1B0 /* libPods.a in Frameworks */, 80 | ); 81 | runOnlyForDeploymentPostprocessing = 0; 82 | }; 83 | 091FEAFE199C11A600505B79 /* Frameworks */ = { 84 | isa = PBXFrameworksBuildPhase; 85 | buildActionMask = 2147483647; 86 | files = ( 87 | 091FEB03199C11A600505B79 /* XCTest.framework in Frameworks */, 88 | 091FEB05199C11A600505B79 /* UIKit.framework in Frameworks */, 89 | 091FEB04199C11A600505B79 /* Foundation.framework in Frameworks */, 90 | 05DB335C13C16470804021DF /* libPods.a in Frameworks */, 91 | ); 92 | runOnlyForDeploymentPostprocessing = 0; 93 | }; 94 | /* End PBXFrameworksBuildPhase section */ 95 | 96 | /* Begin PBXGroup section */ 97 | 091FEAD7199C11A600505B79 = { 98 | isa = PBXGroup; 99 | children = ( 100 | 091FEB18199C12B300505B79 /* OHAttributedStringAdditions.podspec */, 101 | 0925DAC0199C5AE6001D9F8B /* README.md */, 102 | 091FEAE9199C11A600505B79 /* Demo */, 103 | 091FEB08199C11A600505B79 /* Unit Tests */, 104 | 091FEAE2199C11A600505B79 /* Frameworks */, 105 | 091FEAE1199C11A600505B79 /* Products */, 106 | 1A2AE2D26335DB6BA26A56AF /* Pods */, 107 | ); 108 | sourceTree = ""; 109 | }; 110 | 091FEAE1199C11A600505B79 /* Products */ = { 111 | isa = PBXGroup; 112 | children = ( 113 | 091FEAE0199C11A600505B79 /* AttributedStringDemo.app */, 114 | 091FEB01199C11A600505B79 /* UnitTests.xctest */, 115 | ); 116 | name = Products; 117 | sourceTree = ""; 118 | }; 119 | 091FEAE2199C11A600505B79 /* Frameworks */ = { 120 | isa = PBXGroup; 121 | children = ( 122 | 091FEAE3199C11A600505B79 /* Foundation.framework */, 123 | 091FEAE5199C11A600505B79 /* CoreGraphics.framework */, 124 | 091FEAE7199C11A600505B79 /* UIKit.framework */, 125 | 091FEB02199C11A600505B79 /* XCTest.framework */, 126 | 6FF2008EC8614D5D8BD46C84 /* libPods-AttributedStringDemoTests.a */, 127 | 48842986E011607035CF900E /* libPods-AttributedStringDemo.a */, 128 | 1104688BD199A34CE48E58F4 /* libPods.a */, 129 | 0C4F6E012205CD8A335CB820 /* libPods-UnitTests.a */, 130 | ); 131 | name = Frameworks; 132 | sourceTree = ""; 133 | }; 134 | 091FEAE9199C11A600505B79 /* Demo */ = { 135 | isa = PBXGroup; 136 | children = ( 137 | 091FEAF2199C11A600505B79 /* ASDAppDelegate.h */, 138 | 091FEAF3199C11A600505B79 /* ASDAppDelegate.m */, 139 | 091FEAF8199C11A600505B79 /* ASDViewController.h */, 140 | 091FEAF9199C11A600505B79 /* ASDViewController.m */, 141 | 091FEB19199C135100505B79 /* ASDViewController.xib */, 142 | 091FEAFB199C11A600505B79 /* Images.xcassets */, 143 | 091FEAEA199C11A600505B79 /* Supporting Files */, 144 | ); 145 | name = Demo; 146 | path = AttributedStringDemo; 147 | sourceTree = ""; 148 | }; 149 | 091FEAEA199C11A600505B79 /* Supporting Files */ = { 150 | isa = PBXGroup; 151 | children = ( 152 | 091FEAEB199C11A600505B79 /* AttributedStringDemo-Info.plist */, 153 | 091FEAEF199C11A600505B79 /* main.m */, 154 | 091FEAF1199C11A600505B79 /* AttributedStringDemo-Prefix.pch */, 155 | ); 156 | name = "Supporting Files"; 157 | sourceTree = ""; 158 | }; 159 | 091FEB08199C11A600505B79 /* Unit Tests */ = { 160 | isa = PBXGroup; 161 | children = ( 162 | 09A971E919BCB4DA00F82C83 /* OHASATestHelper.h */, 163 | 09A971EA19BCB4DA00F82C83 /* OHASATestHelper.m */, 164 | 091FEB0E199C11A600505B79 /* UIFontTests.m */, 165 | 094443F6199FE3CF00324F4A /* NSAttributedStringTests.m */, 166 | 094443F819A00D0000324F4A /* NSMutableAttributedStringTests.m */, 167 | 091FEB09199C11A600505B79 /* Supporting Files */, 168 | ); 169 | name = "Unit Tests"; 170 | path = UnitTests; 171 | sourceTree = ""; 172 | }; 173 | 091FEB09199C11A600505B79 /* Supporting Files */ = { 174 | isa = PBXGroup; 175 | children = ( 176 | 091FEB0A199C11A600505B79 /* UnitTests-Info.plist */, 177 | ); 178 | name = "Supporting Files"; 179 | sourceTree = ""; 180 | }; 181 | 1A2AE2D26335DB6BA26A56AF /* Pods */ = { 182 | isa = PBXGroup; 183 | children = ( 184 | 091DC22A1A3C90DF009A6103 /* Pods.debug.xcconfig */, 185 | 091DC22B1A3C90DF009A6103 /* Pods.release.xcconfig */, 186 | ); 187 | name = Pods; 188 | sourceTree = ""; 189 | }; 190 | /* End PBXGroup section */ 191 | 192 | /* Begin PBXNativeTarget section */ 193 | 091FEADF199C11A600505B79 /* AttributedStringDemo */ = { 194 | isa = PBXNativeTarget; 195 | buildConfigurationList = 091FEB12199C11A600505B79 /* Build configuration list for PBXNativeTarget "AttributedStringDemo" */; 196 | buildPhases = ( 197 | 4E65AE64CAA466E473C41799 /* Check Pods Manifest.lock */, 198 | 091FEADC199C11A600505B79 /* Sources */, 199 | 091FEADD199C11A600505B79 /* Frameworks */, 200 | 091FEADE199C11A600505B79 /* Resources */, 201 | 40D970C5C43146FF96AB7B21 /* Copy Pods Resources */, 202 | ); 203 | buildRules = ( 204 | ); 205 | dependencies = ( 206 | ); 207 | name = AttributedStringDemo; 208 | productName = AttributedStringDemo; 209 | productReference = 091FEAE0199C11A600505B79 /* AttributedStringDemo.app */; 210 | productType = "com.apple.product-type.application"; 211 | }; 212 | 091FEB00199C11A600505B79 /* UnitTests */ = { 213 | isa = PBXNativeTarget; 214 | buildConfigurationList = 091FEB15199C11A600505B79 /* Build configuration list for PBXNativeTarget "UnitTests" */; 215 | buildPhases = ( 216 | 27D36005FBBD45BAB094582C /* Check Pods Manifest.lock */, 217 | 091FEAFD199C11A600505B79 /* Sources */, 218 | 091FEAFE199C11A600505B79 /* Frameworks */, 219 | 091FEAFF199C11A600505B79 /* Resources */, 220 | B9D0DE37C4E641EAAE684694 /* Copy Pods Resources */, 221 | ); 222 | buildRules = ( 223 | ); 224 | dependencies = ( 225 | 091FEB07199C11A600505B79 /* PBXTargetDependency */, 226 | ); 227 | name = UnitTests; 228 | productName = AttributedStringDemoTests; 229 | productReference = 091FEB01199C11A600505B79 /* UnitTests.xctest */; 230 | productType = "com.apple.product-type.bundle.unit-test"; 231 | }; 232 | /* End PBXNativeTarget section */ 233 | 234 | /* Begin PBXProject section */ 235 | 091FEAD8199C11A600505B79 /* Project object */ = { 236 | isa = PBXProject; 237 | attributes = { 238 | CLASSPREFIX = ASD; 239 | LastUpgradeCheck = 0610; 240 | ORGANIZATIONNAME = AliSoftware; 241 | }; 242 | buildConfigurationList = 091FEADB199C11A600505B79 /* Build configuration list for PBXProject "AttributedStringDemo" */; 243 | compatibilityVersion = "Xcode 3.2"; 244 | developmentRegion = English; 245 | hasScannedForEncodings = 0; 246 | knownRegions = ( 247 | en, 248 | Base, 249 | ); 250 | mainGroup = 091FEAD7199C11A600505B79; 251 | productRefGroup = 091FEAE1199C11A600505B79 /* Products */; 252 | projectDirPath = ""; 253 | projectRoot = ""; 254 | targets = ( 255 | 091FEADF199C11A600505B79 /* AttributedStringDemo */, 256 | 091FEB00199C11A600505B79 /* UnitTests */, 257 | ); 258 | }; 259 | /* End PBXProject section */ 260 | 261 | /* Begin PBXResourcesBuildPhase section */ 262 | 091FEADE199C11A600505B79 /* Resources */ = { 263 | isa = PBXResourcesBuildPhase; 264 | buildActionMask = 2147483647; 265 | files = ( 266 | 091FEAFC199C11A600505B79 /* Images.xcassets in Resources */, 267 | 091FEB1A199C135100505B79 /* ASDViewController.xib in Resources */, 268 | ); 269 | runOnlyForDeploymentPostprocessing = 0; 270 | }; 271 | 091FEAFF199C11A600505B79 /* Resources */ = { 272 | isa = PBXResourcesBuildPhase; 273 | buildActionMask = 2147483647; 274 | files = ( 275 | ); 276 | runOnlyForDeploymentPostprocessing = 0; 277 | }; 278 | /* End PBXResourcesBuildPhase section */ 279 | 280 | /* Begin PBXShellScriptBuildPhase section */ 281 | 27D36005FBBD45BAB094582C /* Check Pods Manifest.lock */ = { 282 | isa = PBXShellScriptBuildPhase; 283 | buildActionMask = 2147483647; 284 | files = ( 285 | ); 286 | inputPaths = ( 287 | ); 288 | name = "Check Pods Manifest.lock"; 289 | outputPaths = ( 290 | ); 291 | runOnlyForDeploymentPostprocessing = 0; 292 | shellPath = /bin/sh; 293 | 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"; 294 | showEnvVarsInLog = 0; 295 | }; 296 | 40D970C5C43146FF96AB7B21 /* Copy Pods Resources */ = { 297 | isa = PBXShellScriptBuildPhase; 298 | buildActionMask = 2147483647; 299 | files = ( 300 | ); 301 | inputPaths = ( 302 | ); 303 | name = "Copy Pods Resources"; 304 | outputPaths = ( 305 | ); 306 | runOnlyForDeploymentPostprocessing = 0; 307 | shellPath = /bin/sh; 308 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n"; 309 | showEnvVarsInLog = 0; 310 | }; 311 | 4E65AE64CAA466E473C41799 /* Check Pods Manifest.lock */ = { 312 | isa = PBXShellScriptBuildPhase; 313 | buildActionMask = 2147483647; 314 | files = ( 315 | ); 316 | inputPaths = ( 317 | ); 318 | name = "Check Pods Manifest.lock"; 319 | outputPaths = ( 320 | ); 321 | runOnlyForDeploymentPostprocessing = 0; 322 | shellPath = /bin/sh; 323 | 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"; 324 | showEnvVarsInLog = 0; 325 | }; 326 | B9D0DE37C4E641EAAE684694 /* Copy Pods Resources */ = { 327 | isa = PBXShellScriptBuildPhase; 328 | buildActionMask = 2147483647; 329 | files = ( 330 | ); 331 | inputPaths = ( 332 | ); 333 | name = "Copy Pods Resources"; 334 | outputPaths = ( 335 | ); 336 | runOnlyForDeploymentPostprocessing = 0; 337 | shellPath = /bin/sh; 338 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n"; 339 | showEnvVarsInLog = 0; 340 | }; 341 | /* End PBXShellScriptBuildPhase section */ 342 | 343 | /* Begin PBXSourcesBuildPhase section */ 344 | 091FEADC199C11A600505B79 /* Sources */ = { 345 | isa = PBXSourcesBuildPhase; 346 | buildActionMask = 2147483647; 347 | files = ( 348 | 091FEAFA199C11A600505B79 /* ASDViewController.m in Sources */, 349 | 091FEAF0199C11A600505B79 /* main.m in Sources */, 350 | 091FEAF4199C11A600505B79 /* ASDAppDelegate.m in Sources */, 351 | ); 352 | runOnlyForDeploymentPostprocessing = 0; 353 | }; 354 | 091FEAFD199C11A600505B79 /* Sources */ = { 355 | isa = PBXSourcesBuildPhase; 356 | buildActionMask = 2147483647; 357 | files = ( 358 | 09A971EC19BCB61300F82C83 /* OHASATestHelper.m in Sources */, 359 | 094443F7199FE3CF00324F4A /* NSAttributedStringTests.m in Sources */, 360 | 094443F919A00D0000324F4A /* NSMutableAttributedStringTests.m in Sources */, 361 | 091FEB0F199C11A600505B79 /* UIFontTests.m in Sources */, 362 | ); 363 | runOnlyForDeploymentPostprocessing = 0; 364 | }; 365 | /* End PBXSourcesBuildPhase section */ 366 | 367 | /* Begin PBXTargetDependency section */ 368 | 091FEB07199C11A600505B79 /* PBXTargetDependency */ = { 369 | isa = PBXTargetDependency; 370 | target = 091FEADF199C11A600505B79 /* AttributedStringDemo */; 371 | targetProxy = 091FEB06199C11A600505B79 /* PBXContainerItemProxy */; 372 | }; 373 | /* End PBXTargetDependency section */ 374 | 375 | /* Begin XCBuildConfiguration section */ 376 | 091FEB10199C11A600505B79 /* Debug */ = { 377 | isa = XCBuildConfiguration; 378 | buildSettings = { 379 | ALWAYS_SEARCH_USER_PATHS = NO; 380 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 381 | CLANG_CXX_LIBRARY = "libc++"; 382 | CLANG_ENABLE_MODULES = YES; 383 | CLANG_ENABLE_OBJC_ARC = YES; 384 | CLANG_WARN_BOOL_CONVERSION = YES; 385 | CLANG_WARN_CONSTANT_CONVERSION = YES; 386 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 387 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 388 | CLANG_WARN_EMPTY_BODY = YES; 389 | CLANG_WARN_ENUM_CONVERSION = YES; 390 | CLANG_WARN_INT_CONVERSION = YES; 391 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 392 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 393 | CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; 394 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 395 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 396 | COPY_PHASE_STRIP = NO; 397 | GCC_C_LANGUAGE_STANDARD = gnu99; 398 | GCC_DYNAMIC_NO_PIC = NO; 399 | GCC_OPTIMIZATION_LEVEL = 0; 400 | GCC_PREPROCESSOR_DEFINITIONS = ( 401 | "DEBUG=1", 402 | "$(inherited)", 403 | ); 404 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 405 | GCC_TREAT_WARNINGS_AS_ERRORS = YES; 406 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 407 | GCC_WARN_ABOUT_MISSING_NEWLINE = YES; 408 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 409 | GCC_WARN_SHADOW = YES; 410 | GCC_WARN_SIGN_COMPARE = YES; 411 | GCC_WARN_UNDECLARED_SELECTOR = YES; 412 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 413 | GCC_WARN_UNUSED_FUNCTION = YES; 414 | GCC_WARN_UNUSED_VARIABLE = YES; 415 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 416 | ONLY_ACTIVE_ARCH = YES; 417 | SDKROOT = iphoneos; 418 | }; 419 | name = Debug; 420 | }; 421 | 091FEB11199C11A600505B79 /* Release */ = { 422 | isa = XCBuildConfiguration; 423 | buildSettings = { 424 | ALWAYS_SEARCH_USER_PATHS = NO; 425 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 426 | CLANG_CXX_LIBRARY = "libc++"; 427 | CLANG_ENABLE_MODULES = YES; 428 | CLANG_ENABLE_OBJC_ARC = YES; 429 | CLANG_WARN_BOOL_CONVERSION = YES; 430 | CLANG_WARN_CONSTANT_CONVERSION = YES; 431 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 432 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 433 | CLANG_WARN_EMPTY_BODY = YES; 434 | CLANG_WARN_ENUM_CONVERSION = YES; 435 | CLANG_WARN_INT_CONVERSION = YES; 436 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 437 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 438 | CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; 439 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 440 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 441 | COPY_PHASE_STRIP = YES; 442 | ENABLE_NS_ASSERTIONS = NO; 443 | GCC_C_LANGUAGE_STANDARD = gnu99; 444 | GCC_TREAT_WARNINGS_AS_ERRORS = YES; 445 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 446 | GCC_WARN_ABOUT_MISSING_NEWLINE = YES; 447 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 448 | GCC_WARN_SHADOW = YES; 449 | GCC_WARN_SIGN_COMPARE = YES; 450 | GCC_WARN_UNDECLARED_SELECTOR = YES; 451 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 452 | GCC_WARN_UNUSED_FUNCTION = YES; 453 | GCC_WARN_UNUSED_VARIABLE = YES; 454 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 455 | SDKROOT = iphoneos; 456 | VALIDATE_PRODUCT = YES; 457 | }; 458 | name = Release; 459 | }; 460 | 091FEB13199C11A600505B79 /* Debug */ = { 461 | isa = XCBuildConfiguration; 462 | baseConfigurationReference = 091DC22A1A3C90DF009A6103 /* Pods.debug.xcconfig */; 463 | buildSettings = { 464 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 465 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 466 | GCC_PRECOMPILE_PREFIX_HEADER = NO; 467 | GCC_PREFIX_HEADER = "AttributedStringDemo/AttributedStringDemo-Prefix.pch"; 468 | INFOPLIST_FILE = "AttributedStringDemo/AttributedStringDemo-Info.plist"; 469 | PRODUCT_NAME = "$(TARGET_NAME)"; 470 | WRAPPER_EXTENSION = app; 471 | }; 472 | name = Debug; 473 | }; 474 | 091FEB14199C11A600505B79 /* Release */ = { 475 | isa = XCBuildConfiguration; 476 | baseConfigurationReference = 091DC22B1A3C90DF009A6103 /* Pods.release.xcconfig */; 477 | buildSettings = { 478 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 479 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 480 | GCC_PRECOMPILE_PREFIX_HEADER = NO; 481 | GCC_PREFIX_HEADER = "AttributedStringDemo/AttributedStringDemo-Prefix.pch"; 482 | INFOPLIST_FILE = "AttributedStringDemo/AttributedStringDemo-Info.plist"; 483 | PRODUCT_NAME = "$(TARGET_NAME)"; 484 | WRAPPER_EXTENSION = app; 485 | }; 486 | name = Release; 487 | }; 488 | 091FEB16199C11A600505B79 /* Debug */ = { 489 | isa = XCBuildConfiguration; 490 | baseConfigurationReference = 091DC22A1A3C90DF009A6103 /* Pods.debug.xcconfig */; 491 | buildSettings = { 492 | FRAMEWORK_SEARCH_PATHS = ( 493 | "$(SDKROOT)/Developer/Library/Frameworks", 494 | "$(inherited)", 495 | "$(DEVELOPER_FRAMEWORKS_DIR)", 496 | ); 497 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 498 | GCC_PREPROCESSOR_DEFINITIONS = ( 499 | "DEBUG=1", 500 | "$(inherited)", 501 | ); 502 | INFOPLIST_FILE = "UnitTests/UnitTests-Info.plist"; 503 | PRODUCT_NAME = "$(TARGET_NAME)"; 504 | WRAPPER_EXTENSION = xctest; 505 | }; 506 | name = Debug; 507 | }; 508 | 091FEB17199C11A600505B79 /* Release */ = { 509 | isa = XCBuildConfiguration; 510 | baseConfigurationReference = 091DC22B1A3C90DF009A6103 /* Pods.release.xcconfig */; 511 | buildSettings = { 512 | FRAMEWORK_SEARCH_PATHS = ( 513 | "$(SDKROOT)/Developer/Library/Frameworks", 514 | "$(inherited)", 515 | "$(DEVELOPER_FRAMEWORKS_DIR)", 516 | ); 517 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 518 | INFOPLIST_FILE = "UnitTests/UnitTests-Info.plist"; 519 | PRODUCT_NAME = "$(TARGET_NAME)"; 520 | WRAPPER_EXTENSION = xctest; 521 | }; 522 | name = Release; 523 | }; 524 | /* End XCBuildConfiguration section */ 525 | 526 | /* Begin XCConfigurationList section */ 527 | 091FEADB199C11A600505B79 /* Build configuration list for PBXProject "AttributedStringDemo" */ = { 528 | isa = XCConfigurationList; 529 | buildConfigurations = ( 530 | 091FEB10199C11A600505B79 /* Debug */, 531 | 091FEB11199C11A600505B79 /* Release */, 532 | ); 533 | defaultConfigurationIsVisible = 0; 534 | defaultConfigurationName = Release; 535 | }; 536 | 091FEB12199C11A600505B79 /* Build configuration list for PBXNativeTarget "AttributedStringDemo" */ = { 537 | isa = XCConfigurationList; 538 | buildConfigurations = ( 539 | 091FEB13199C11A600505B79 /* Debug */, 540 | 091FEB14199C11A600505B79 /* Release */, 541 | ); 542 | defaultConfigurationIsVisible = 0; 543 | defaultConfigurationName = Release; 544 | }; 545 | 091FEB15199C11A600505B79 /* Build configuration list for PBXNativeTarget "UnitTests" */ = { 546 | isa = XCConfigurationList; 547 | buildConfigurations = ( 548 | 091FEB16199C11A600505B79 /* Debug */, 549 | 091FEB17199C11A600505B79 /* Release */, 550 | ); 551 | defaultConfigurationIsVisible = 0; 552 | defaultConfigurationName = Release; 553 | }; 554 | /* End XCConfigurationList section */ 555 | }; 556 | rootObject = 091FEAD8199C11A600505B79 /* Project object */; 557 | } 558 | -------------------------------------------------------------------------------- /Example/AttributedStringDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/AttributedStringDemo.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/AttributedStringDemo.xcodeproj/xcshareddata/xcschemes/AttributedStringDemo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 43 | 49 | 50 | 51 | 52 | 53 | 58 | 59 | 61 | 67 | 68 | 69 | 70 | 71 | 77 | 78 | 79 | 80 | 89 | 90 | 96 | 97 | 98 | 99 | 100 | 101 | 107 | 108 | 114 | 115 | 116 | 117 | 119 | 120 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /Example/AttributedStringDemo.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Example/AttributedStringDemo.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/AttributedStringDemo/ASDAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // ASDAppDelegate.h 3 | // AttributedStringDemo 4 | // 5 | // Created by Olivier Halligon on 13/08/2014. 6 | // Copyright (c) 2014 AliSoftware. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ASDAppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Example/AttributedStringDemo/ASDAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // ASDAppDelegate.m 3 | // AttributedStringDemo 4 | // 5 | // Created by Olivier Halligon on 13/08/2014. 6 | // Copyright (c) 2014 AliSoftware. All rights reserved. 7 | // 8 | 9 | #import "ASDAppDelegate.h" 10 | #import "ASDViewController.h" 11 | 12 | @implementation ASDAppDelegate 13 | 14 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 15 | { 16 | // Override point for customization after application launch. 17 | return YES; 18 | } 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /Example/AttributedStringDemo/ASDViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ASDViewController.h 3 | // AttributedStringDemo 4 | // 5 | // Created by Olivier Halligon on 13/08/2014. 6 | // Copyright (c) 2014 AliSoftware. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ASDViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Example/AttributedStringDemo/ASDViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ASDViewController.m 3 | // AttributedStringDemo 4 | // 5 | // Created by Olivier Halligon on 13/08/2014. 6 | // Copyright (c) 2014 AliSoftware. All rights reserved. 7 | // 8 | 9 | #import "ASDViewController.h" 10 | 11 | #import 12 | 13 | @interface ASDViewController () 14 | @property (weak, nonatomic) IBOutlet UITextView* textView; 15 | @property (weak, nonatomic) IBOutlet NSLayoutConstraint* bottomDistance; 16 | @property (weak, nonatomic) IBOutlet UILabel *footerLabel; 17 | @end 18 | 19 | @implementation ASDViewController 20 | 21 | - (void)viewDidLoad 22 | { 23 | [super viewDidLoad]; 24 | // Do any additional setup after loading the view, typically from a nib. 25 | 26 | self.textView.attributedText = [self originalString]; 27 | 28 | NSMutableAttributedString* mas = [self.footerLabel.attributedText mutableCopy]; 29 | NSRange range = NSMakeRange(10, 20); 30 | [mas setURL:[NSURL URLWithString:@"https://github.com/AliSoftware/OHAttributedStringAdditions/wiki"] range:range]; 31 | [mas setTextColor:[UIColor blueColor] range:range]; 32 | [mas setTextUnderlined:YES range:range]; 33 | self.footerLabel.attributedText = [mas copy]; 34 | 35 | [[NSNotificationCenter defaultCenter] addObserver:self 36 | selector:@selector(keyboardAnimation:) 37 | name:UIKeyboardWillChangeFrameNotification 38 | object:nil]; 39 | } 40 | 41 | - (void)dealloc 42 | { 43 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 44 | } 45 | 46 | - (NSAttributedString*)originalString 47 | { 48 | static NSAttributedString* originalString; 49 | static dispatch_once_t onceToken; 50 | #define LIST_TITLE @"Easily get, set or change" 51 | dispatch_once(&onceToken, ^{ 52 | NSString* html = @"" 53 | "Hello you!
" 54 | "
" 55 | "

This is OHAttributedStringAdditions," 56 | " a cool category that lets you manipulate NSAttributedString" 57 | " with nicer methods(1).

" 58 | "

For example you can change the font, make text" 59 | " bold, italics or underlined, …

" 60 | "

Amazing, isn't it?

" 61 | "

You can even indent the first line of your" 62 | " paragraphs to make nice text layouts, justify paragraphs, and a lot more!

" 63 | "

(1)" LIST_TITLE ":

    " 64 | "
  • Fonts : setFont:range:
  • " 65 | "
  • Colors : setTextColor:range:
  • " 66 | "
  • Style : setTextBold:range:, setTextUnderlined:range:, …
  • " 67 | "
  • URLs : setURL:range:
  • " 68 | "
  • Paragraph style (indentation, text alignment, …)
  • " 69 | "
  • And much more
  • " 70 | "

" 71 | "

Note: Part of this attributed string is created" 72 | " by using basic HTML code, and some other attribues" 73 | " are set by code to demonstrate the usage of" 74 | " OHAttributedStringAdditions methods.

" 75 | "
Don't hesitate to try the search field at the top of the screen." 76 | " Searched text will be highlighted dynamically!
"; 77 | 78 | NSMutableAttributedString* str = [NSMutableAttributedString attributedStringWithHTML:html]; 79 | 80 | // Scale the font size by +50% 81 | [str enumerateFontsInRange:NSMakeRange(0,str.length) 82 | includeUndefined:YES 83 | usingBlock:^(UIFont *font, NSRange range, BOOL *stop) 84 | { 85 | if (!font) font = [NSAttributedString defaultFont]; 86 | UIFont* newFont = [font fontWithSize:font.pointSize * 1.5f] ; 87 | [str setFont:newFont range:range]; 88 | }]; 89 | 90 | [str setTextUnderlined:YES range:[str.string rangeOfString:@"Amazing"]]; 91 | NSRange listTitleRange = [str.string rangeOfString:LIST_TITLE]; 92 | [str setTextColor:[UIColor colorWithRed:0 green:0.5 blue:0 alpha:1.] range:listTitleRange]; 93 | [str setTextUnderlineStyle:NSUnderlineStyleThick|NSUnderlinePatternDot range:listTitleRange]; 94 | 95 | [str changeParagraphStylesInRange:NSMakeRange(0, str.length) 96 | withBlock:^(NSMutableParagraphStyle* style, NSRange aRange) 97 | { 98 | // For example, justify only paragraphs that have a indentation 99 | if (style.firstLineHeadIndent > 0) 100 | { 101 | style.alignment = NSTextAlignmentJustified; 102 | } 103 | }]; 104 | 105 | originalString = [str copy]; 106 | }); 107 | return originalString; 108 | } 109 | 110 | // MARK: Tap on a links 111 | 112 | // Links in TextView are already supported by UITextViewDelegate 113 | - (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange 114 | { 115 | // Intercept taps on links to display an alert instead of opening it in Safari 116 | [[[UIAlertView alloc] initWithTitle:@"URL Tapped" 117 | message:URL.absoluteString 118 | delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil] show]; 119 | return NO; 120 | } 121 | 122 | // Links on UILabel have to use UITapGestureRecognizer (here added via the XIB) 123 | - (IBAction)tapOnFooter:(UITapGestureRecognizer *)tapGR 124 | { 125 | if (tapGR.state == UIGestureRecognizerStateEnded) 126 | { 127 | CGPoint tapPoint = [tapGR locationInView:self.footerLabel]; 128 | NSUInteger pos = [self.footerLabel characterIndexAtPoint:tapPoint]; 129 | if (pos != NSNotFound) 130 | { 131 | NSURL* urlTapped = [self.footerLabel.attributedText URLAtIndex:pos effectiveRange:NULL]; 132 | if (urlTapped) [[UIApplication sharedApplication] openURL:urlTapped]; 133 | } 134 | } 135 | } 136 | 137 | 138 | // MARK: Text Search 139 | 140 | - (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText 141 | { 142 | if (searchText.length > 0) 143 | { 144 | NSMutableAttributedString* highlightedString = [[self originalString] mutableCopy]; 145 | 146 | NSRegularExpressionOptions options = NSRegularExpressionCaseInsensitive|NSRegularExpressionIgnoreMetacharacters; 147 | NSRegularExpression* regex = [NSRegularExpression regularExpressionWithPattern:searchText 148 | options:options 149 | error:NULL]; 150 | [regex enumerateMatchesInString:self.textView.text 151 | options:0 152 | range:NSMakeRange(0, self.textView.text.length) 153 | usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) 154 | { 155 | [highlightedString setTextBackgroundColor:[UIColor yellowColor] range:result.range]; 156 | }]; 157 | 158 | self.textView.attributedText = highlightedString; 159 | } 160 | else 161 | { 162 | // Reset to unhighlighted string 163 | self.textView.attributedText = [self originalString]; 164 | } 165 | } 166 | 167 | - (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar 168 | { 169 | [searchBar resignFirstResponder]; 170 | } 171 | 172 | -(void)searchBarCancelButtonClicked:(UISearchBar *)searchBar 173 | { 174 | self.textView.attributedText = [self originalString]; 175 | [searchBar resignFirstResponder]; 176 | } 177 | 178 | - (void)keyboardAnimation:(NSNotification*)notif 179 | { 180 | NSTimeInterval animDuration = [notif.userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue]; 181 | CGRect endFrame = [notif.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue]; 182 | 183 | self.bottomDistance.constant = self.view.window.bounds.size.height - endFrame.origin.y; 184 | [UIView animateWithDuration:animDuration animations:^{ 185 | [self.textView layoutIfNeeded]; 186 | }]; 187 | } 188 | 189 | @end 190 | -------------------------------------------------------------------------------- /Example/AttributedStringDemo/ASDViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda. 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /Example/AttributedStringDemo/AttributedStringDemo-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | com.alisoftware.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | NSMainNibFile 28 | ASDViewController 29 | UILaunchStoryboardName 30 | ASDViewController 31 | UIRequiredDeviceCapabilities 32 | 33 | armv7 34 | 35 | UISupportedInterfaceOrientations 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationLandscapeLeft 39 | UIInterfaceOrientationLandscapeRight 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /Example/AttributedStringDemo/AttributedStringDemo-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 | -------------------------------------------------------------------------------- /Example/AttributedStringDemo/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 | } -------------------------------------------------------------------------------- /Example/AttributedStringDemo/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 | } -------------------------------------------------------------------------------- /Example/AttributedStringDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // AttributedStringDemo 4 | // 5 | // Created by Olivier Halligon on 13/08/2014. 6 | // Copyright (c) 2014 AliSoftware. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "ASDAppDelegate.h" 12 | 13 | int main(int argc, char * argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([ASDAppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Example/Podfile: -------------------------------------------------------------------------------- 1 | xcodeproj 'AttributedStringDemo.xcodeproj' 2 | platform :ios, "7.0" 3 | 4 | link_with 'AttributedStringDemo', 'UnitTests' 5 | pod 'OHAttributedStringAdditions', :path => '..' 6 | -------------------------------------------------------------------------------- /Example/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - OHAttributedStringAdditions (1.3.0): 3 | - OHAttributedStringAdditions/Base (= 1.3.0) 4 | - OHAttributedStringAdditions/UILabel (= 1.3.0) 5 | - OHAttributedStringAdditions/Base (1.3.0) 6 | - OHAttributedStringAdditions/UILabel (1.3.0): 7 | - OHAttributedStringAdditions/Base 8 | 9 | DEPENDENCIES: 10 | - OHAttributedStringAdditions (from `..`) 11 | 12 | EXTERNAL SOURCES: 13 | OHAttributedStringAdditions: 14 | :path: .. 15 | 16 | SPEC CHECKSUMS: 17 | OHAttributedStringAdditions: d810cff43f5dd0054df62de2e7bf472c9a2b9d3e 18 | 19 | COCOAPODS: 0.36.0 20 | -------------------------------------------------------------------------------- /Example/Pods/Headers/Private/OHAttributedStringAdditions/NSAttributedString+OHAdditions.h: -------------------------------------------------------------------------------- 1 | ../../../../../Source/NSAttributedString+OHAdditions.h -------------------------------------------------------------------------------- /Example/Pods/Headers/Private/OHAttributedStringAdditions/NSMutableAttributedString+OHAdditions.h: -------------------------------------------------------------------------------- 1 | ../../../../../Source/NSMutableAttributedString+OHAdditions.h -------------------------------------------------------------------------------- /Example/Pods/Headers/Private/OHAttributedStringAdditions/OHAttributedStringAdditions.h: -------------------------------------------------------------------------------- 1 | ../../../../../Source/OHAttributedStringAdditions.h -------------------------------------------------------------------------------- /Example/Pods/Headers/Private/OHAttributedStringAdditions/UIFont+OHAdditions.h: -------------------------------------------------------------------------------- 1 | ../../../../../Source/UIFont+OHAdditions.h -------------------------------------------------------------------------------- /Example/Pods/Headers/Private/OHAttributedStringAdditions/UILabel+OHAdditions.h: -------------------------------------------------------------------------------- 1 | ../../../../../Source/UILabel+OHAdditions.h -------------------------------------------------------------------------------- /Example/Pods/Headers/Public/OHAttributedStringAdditions/NSAttributedString+OHAdditions.h: -------------------------------------------------------------------------------- 1 | ../../../../../Source/NSAttributedString+OHAdditions.h -------------------------------------------------------------------------------- /Example/Pods/Headers/Public/OHAttributedStringAdditions/NSMutableAttributedString+OHAdditions.h: -------------------------------------------------------------------------------- 1 | ../../../../../Source/NSMutableAttributedString+OHAdditions.h -------------------------------------------------------------------------------- /Example/Pods/Headers/Public/OHAttributedStringAdditions/OHAttributedStringAdditions.h: -------------------------------------------------------------------------------- 1 | ../../../../../Source/OHAttributedStringAdditions.h -------------------------------------------------------------------------------- /Example/Pods/Headers/Public/OHAttributedStringAdditions/UIFont+OHAdditions.h: -------------------------------------------------------------------------------- 1 | ../../../../../Source/UIFont+OHAdditions.h -------------------------------------------------------------------------------- /Example/Pods/Headers/Public/OHAttributedStringAdditions/UILabel+OHAdditions.h: -------------------------------------------------------------------------------- 1 | ../../../../../Source/UILabel+OHAdditions.h -------------------------------------------------------------------------------- /Example/Pods/Local Podspecs/OHAttributedStringAdditions.podspec.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "OHAttributedStringAdditions", 3 | "version": "1.3.0", 4 | "summary": "Categories on NSAttributedString to add a lot of very useful convenience methods.", 5 | "description": "This pod contains categories on `NSAttributedString` to add a lot of very useful\nconvenience methods to manipulate attributed strings.\n\nThe includes convenience methods to:\n * set attributes on a given range\n * get attributes at a given index\n\nConvenience methods are available for a lot of common attributes, including:\n * Fonts\n * Text Foreground and Background Colors\n * Text Style (bold, italics, underline)\n * Links (URLs)\n * Baseline offset, subscript, superscript\n * Text alignment, linebreak mode, character spacing\n * Paragraph Style (text indent, linespacing, …)\n * etc.\n", 6 | "homepage": "https://github.com/AliSoftware/OHAttributedStringAdditions", 7 | "license": { 8 | "type": "MIT", 9 | "file": "LICENSE" 10 | }, 11 | "authors": { 12 | "Olivier Halligon": "olivier@halligon.net" 13 | }, 14 | "social_media_url": "http://twitter.com/aligatr", 15 | "platforms": { 16 | "ios": "7.0" 17 | }, 18 | "source": { 19 | "git": "https://github.com/AliSoftware/OHAttributedStringAdditions.git", 20 | "tag": "1.3.0" 21 | }, 22 | "requires_arc": true, 23 | "subspecs": [ 24 | { 25 | "name": "Base", 26 | "source_files": [ 27 | "Source/OHAttributedStringAdditions.h", 28 | "Source/{NSAttributedString,NSMutableAttributedString,UIFont}+OHAdditions.{h,m}" 29 | ] 30 | }, 31 | { 32 | "name": "UILabel", 33 | "source_files": "Source/UILabel+OHAdditions.{h,m}", 34 | "dependencies": { 35 | "OHAttributedStringAdditions/Base": [ 36 | 37 | ] 38 | } 39 | } 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /Example/Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - OHAttributedStringAdditions (1.3.0): 3 | - OHAttributedStringAdditions/Base (= 1.3.0) 4 | - OHAttributedStringAdditions/UILabel (= 1.3.0) 5 | - OHAttributedStringAdditions/Base (1.3.0) 6 | - OHAttributedStringAdditions/UILabel (1.3.0): 7 | - OHAttributedStringAdditions/Base 8 | 9 | DEPENDENCIES: 10 | - OHAttributedStringAdditions (from `..`) 11 | 12 | EXTERNAL SOURCES: 13 | OHAttributedStringAdditions: 14 | :path: .. 15 | 16 | SPEC CHECKSUMS: 17 | OHAttributedStringAdditions: d810cff43f5dd0054df62de2e7bf472c9a2b9d3e 18 | 19 | COCOAPODS: 0.36.0 20 | -------------------------------------------------------------------------------- /Example/Pods/Pods.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 0605D8499077ACA2174A7BC3 /* NSMutableAttributedString+OHAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = CAE2BB42DD950560D765A81C /* NSMutableAttributedString+OHAdditions.h */; }; 11 | 1B4AEE2C2A1A4CAE737DBAFA /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1B64F5E8869E93D36A083D0E /* Foundation.framework */; }; 12 | 2124593D4871E36469596BA1 /* NSAttributedString+OHAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 5926D17671A01394ED1BBDEF /* NSAttributedString+OHAdditions.h */; }; 13 | 231C2348E53AFD20361397A7 /* OHAttributedStringAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 4186D2534B060149A6E235C1 /* OHAttributedStringAdditions.h */; }; 14 | 25EFA894FCAB729EB31F5606 /* UIFont+OHAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 6EC90CB963E963CD21530F7B /* UIFont+OHAdditions.h */; }; 15 | 30F87631880DD8EFACC34667 /* NSMutableAttributedString+OHAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = BE230D04B38FD2AEB0852FDE /* NSMutableAttributedString+OHAdditions.m */; }; 16 | 354E4173B24BB8DE3237DDB1 /* Pods-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 8769CEF18EA4239A15E17E08 /* Pods-dummy.m */; }; 17 | 37FCBABCC1038ED244ECCDEC /* Pods-OHAttributedStringAdditions-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = C930613009EE836D78C77947 /* Pods-OHAttributedStringAdditions-dummy.m */; }; 18 | 519F0CDD757677CDBCFAF7F4 /* NSAttributedString+OHAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 500F90DAE1944C652DBF5B29 /* NSAttributedString+OHAdditions.m */; }; 19 | 64F7F009BD63D01059EB99F3 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1B64F5E8869E93D36A083D0E /* Foundation.framework */; }; 20 | 8EA0B859EFB055F84A3E1ED4 /* UILabel+OHAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 984EAF4CC7D0629D23B498A5 /* UILabel+OHAdditions.h */; }; 21 | 9AF427D6131DC57D3721268D /* UIFont+OHAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = A268CD3735944768CBB101FB /* UIFont+OHAdditions.m */; }; 22 | B9E9D0C465FB21FAB7024D1C /* UILabel+OHAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = A52AEDC08C1CEC5F1CCF1AFE /* UILabel+OHAdditions.m */; }; 23 | /* End PBXBuildFile section */ 24 | 25 | /* Begin PBXContainerItemProxy section */ 26 | E039D9E7A242C7F396BE9B4E /* PBXContainerItemProxy */ = { 27 | isa = PBXContainerItemProxy; 28 | containerPortal = 8B9C3D0F71044B5FD372CFAB /* Project object */; 29 | proxyType = 1; 30 | remoteGlobalIDString = B630051DB34C05310D5E7AEA; 31 | remoteInfo = "Pods-OHAttributedStringAdditions"; 32 | }; 33 | /* End PBXContainerItemProxy section */ 34 | 35 | /* Begin PBXFileReference section */ 36 | 1B64F5E8869E93D36A083D0E /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS7.1.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; 37 | 4186D2534B060149A6E235C1 /* OHAttributedStringAdditions.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = OHAttributedStringAdditions.h; sourceTree = ""; }; 38 | 48EE5962938B9266F611F7E6 /* Pods-resources.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-resources.sh"; sourceTree = ""; }; 39 | 500F90DAE1944C652DBF5B29 /* NSAttributedString+OHAdditions.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "NSAttributedString+OHAdditions.m"; sourceTree = ""; }; 40 | 5926D17671A01394ED1BBDEF /* NSAttributedString+OHAdditions.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "NSAttributedString+OHAdditions.h"; sourceTree = ""; }; 41 | 69C21083782E5B9B65BCAED6 /* Pods-OHAttributedStringAdditions-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-OHAttributedStringAdditions-prefix.pch"; sourceTree = ""; }; 42 | 6EC90CB963E963CD21530F7B /* UIFont+OHAdditions.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "UIFont+OHAdditions.h"; sourceTree = ""; }; 43 | 8769CEF18EA4239A15E17E08 /* Pods-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-dummy.m"; sourceTree = ""; }; 44 | 8DDCD4DB15F67A9A237E9D6C /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Pods.release.xcconfig; sourceTree = ""; }; 45 | 90BEBAE820C26D9422299265 /* Pods-OHAttributedStringAdditions-Private.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-OHAttributedStringAdditions-Private.xcconfig"; sourceTree = ""; }; 46 | 984EAF4CC7D0629D23B498A5 /* UILabel+OHAdditions.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "UILabel+OHAdditions.h"; sourceTree = ""; }; 47 | A268CD3735944768CBB101FB /* UIFont+OHAdditions.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "UIFont+OHAdditions.m"; sourceTree = ""; }; 48 | A52AEDC08C1CEC5F1CCF1AFE /* UILabel+OHAdditions.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "UILabel+OHAdditions.m"; sourceTree = ""; }; 49 | B0C3B97392A6644944FBC06F /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; }; 50 | B67CCA968229B78C358ECAF5 /* Pods-OHAttributedStringAdditions.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-OHAttributedStringAdditions.xcconfig"; sourceTree = ""; }; 51 | BE230D04B38FD2AEB0852FDE /* NSMutableAttributedString+OHAdditions.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "NSMutableAttributedString+OHAdditions.m"; sourceTree = ""; }; 52 | C1CEAD0CF45B09A94354B503 /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Pods.debug.xcconfig; sourceTree = ""; }; 53 | C930613009EE836D78C77947 /* Pods-OHAttributedStringAdditions-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-OHAttributedStringAdditions-dummy.m"; sourceTree = ""; }; 54 | CAE2BB42DD950560D765A81C /* NSMutableAttributedString+OHAdditions.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "NSMutableAttributedString+OHAdditions.h"; sourceTree = ""; }; 55 | CE8C8E1A2D126907A81925FD /* Pods-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-acknowledgements.markdown"; sourceTree = ""; }; 56 | D837E030FA87A9157429DC40 /* libPods-OHAttributedStringAdditions.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-OHAttributedStringAdditions.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 57 | D9F11F1515FA68B221E180CB /* Podfile */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; 58 | E3981D374D44583DA685DE7B /* Pods-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-acknowledgements.plist"; sourceTree = ""; }; 59 | F2227E27B1387F758A38E3A6 /* Pods-environment.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-environment.h"; sourceTree = ""; }; 60 | /* End PBXFileReference section */ 61 | 62 | /* Begin PBXFrameworksBuildPhase section */ 63 | 83177EA619BC14F68D90BC0A /* Frameworks */ = { 64 | isa = PBXFrameworksBuildPhase; 65 | buildActionMask = 2147483647; 66 | files = ( 67 | 64F7F009BD63D01059EB99F3 /* Foundation.framework in Frameworks */, 68 | ); 69 | runOnlyForDeploymentPostprocessing = 0; 70 | }; 71 | 974BBBE722E6CF70FEDE20AD /* Frameworks */ = { 72 | isa = PBXFrameworksBuildPhase; 73 | buildActionMask = 2147483647; 74 | files = ( 75 | 1B4AEE2C2A1A4CAE737DBAFA /* Foundation.framework in Frameworks */, 76 | ); 77 | runOnlyForDeploymentPostprocessing = 0; 78 | }; 79 | /* End PBXFrameworksBuildPhase section */ 80 | 81 | /* Begin PBXGroup section */ 82 | 0CBA8EB78432B224049F986A /* OHAttributedStringAdditions */ = { 83 | isa = PBXGroup; 84 | children = ( 85 | 34A465BC0C3AE4109BB07F33 /* Base */, 86 | ED36A7090A51CA798CCD4990 /* Support Files */, 87 | 2B97902AF5C8F44A5B320BDD /* UILabel */, 88 | ); 89 | name = OHAttributedStringAdditions; 90 | path = ../..; 91 | sourceTree = ""; 92 | }; 93 | 1E67CAD93DDAA1D312FFD665 = { 94 | isa = PBXGroup; 95 | children = ( 96 | D9F11F1515FA68B221E180CB /* Podfile */, 97 | FA6326C49294BEFD58E993AF /* Development Pods */, 98 | 52C42E6C413F10D6BA2820B1 /* Frameworks */, 99 | 4A839CB6C06DB50EA78AF0C6 /* Products */, 100 | 4D875F4D1B68663E567D9A14 /* Targets Support Files */, 101 | ); 102 | sourceTree = ""; 103 | }; 104 | 2B97902AF5C8F44A5B320BDD /* UILabel */ = { 105 | isa = PBXGroup; 106 | children = ( 107 | 89524B7AA4A0D67BB6444CF1 /* Source */, 108 | ); 109 | name = UILabel; 110 | sourceTree = ""; 111 | }; 112 | 34A465BC0C3AE4109BB07F33 /* Base */ = { 113 | isa = PBXGroup; 114 | children = ( 115 | 99FB352DFC77C8A768656F34 /* Source */, 116 | ); 117 | name = Base; 118 | sourceTree = ""; 119 | }; 120 | 4A839CB6C06DB50EA78AF0C6 /* Products */ = { 121 | isa = PBXGroup; 122 | children = ( 123 | B0C3B97392A6644944FBC06F /* libPods.a */, 124 | D837E030FA87A9157429DC40 /* libPods-OHAttributedStringAdditions.a */, 125 | ); 126 | name = Products; 127 | sourceTree = ""; 128 | }; 129 | 4D875F4D1B68663E567D9A14 /* Targets Support Files */ = { 130 | isa = PBXGroup; 131 | children = ( 132 | 64E6C286A732C288BDF109DD /* Pods */, 133 | ); 134 | name = "Targets Support Files"; 135 | sourceTree = ""; 136 | }; 137 | 52C42E6C413F10D6BA2820B1 /* Frameworks */ = { 138 | isa = PBXGroup; 139 | children = ( 140 | 93DAF13F571CBB90564B5564 /* iOS */, 141 | ); 142 | name = Frameworks; 143 | sourceTree = ""; 144 | }; 145 | 64E6C286A732C288BDF109DD /* Pods */ = { 146 | isa = PBXGroup; 147 | children = ( 148 | CE8C8E1A2D126907A81925FD /* Pods-acknowledgements.markdown */, 149 | E3981D374D44583DA685DE7B /* Pods-acknowledgements.plist */, 150 | 8769CEF18EA4239A15E17E08 /* Pods-dummy.m */, 151 | F2227E27B1387F758A38E3A6 /* Pods-environment.h */, 152 | 48EE5962938B9266F611F7E6 /* Pods-resources.sh */, 153 | C1CEAD0CF45B09A94354B503 /* Pods.debug.xcconfig */, 154 | 8DDCD4DB15F67A9A237E9D6C /* Pods.release.xcconfig */, 155 | ); 156 | name = Pods; 157 | path = "Target Support Files/Pods"; 158 | sourceTree = ""; 159 | }; 160 | 89524B7AA4A0D67BB6444CF1 /* Source */ = { 161 | isa = PBXGroup; 162 | children = ( 163 | 984EAF4CC7D0629D23B498A5 /* UILabel+OHAdditions.h */, 164 | A52AEDC08C1CEC5F1CCF1AFE /* UILabel+OHAdditions.m */, 165 | ); 166 | path = Source; 167 | sourceTree = ""; 168 | }; 169 | 93DAF13F571CBB90564B5564 /* iOS */ = { 170 | isa = PBXGroup; 171 | children = ( 172 | 1B64F5E8869E93D36A083D0E /* Foundation.framework */, 173 | ); 174 | name = iOS; 175 | sourceTree = ""; 176 | }; 177 | 99FB352DFC77C8A768656F34 /* Source */ = { 178 | isa = PBXGroup; 179 | children = ( 180 | 5926D17671A01394ED1BBDEF /* NSAttributedString+OHAdditions.h */, 181 | 500F90DAE1944C652DBF5B29 /* NSAttributedString+OHAdditions.m */, 182 | CAE2BB42DD950560D765A81C /* NSMutableAttributedString+OHAdditions.h */, 183 | BE230D04B38FD2AEB0852FDE /* NSMutableAttributedString+OHAdditions.m */, 184 | 4186D2534B060149A6E235C1 /* OHAttributedStringAdditions.h */, 185 | 6EC90CB963E963CD21530F7B /* UIFont+OHAdditions.h */, 186 | A268CD3735944768CBB101FB /* UIFont+OHAdditions.m */, 187 | ); 188 | path = Source; 189 | sourceTree = ""; 190 | }; 191 | ED36A7090A51CA798CCD4990 /* Support Files */ = { 192 | isa = PBXGroup; 193 | children = ( 194 | B67CCA968229B78C358ECAF5 /* Pods-OHAttributedStringAdditions.xcconfig */, 195 | 90BEBAE820C26D9422299265 /* Pods-OHAttributedStringAdditions-Private.xcconfig */, 196 | C930613009EE836D78C77947 /* Pods-OHAttributedStringAdditions-dummy.m */, 197 | 69C21083782E5B9B65BCAED6 /* Pods-OHAttributedStringAdditions-prefix.pch */, 198 | ); 199 | name = "Support Files"; 200 | path = "Example/Pods/Target Support Files/Pods-OHAttributedStringAdditions"; 201 | sourceTree = ""; 202 | }; 203 | FA6326C49294BEFD58E993AF /* Development Pods */ = { 204 | isa = PBXGroup; 205 | children = ( 206 | 0CBA8EB78432B224049F986A /* OHAttributedStringAdditions */, 207 | ); 208 | name = "Development Pods"; 209 | sourceTree = ""; 210 | }; 211 | /* End PBXGroup section */ 212 | 213 | /* Begin PBXHeadersBuildPhase section */ 214 | 291E79D89293D48C7967619C /* Headers */ = { 215 | isa = PBXHeadersBuildPhase; 216 | buildActionMask = 2147483647; 217 | files = ( 218 | 2124593D4871E36469596BA1 /* NSAttributedString+OHAdditions.h in Headers */, 219 | 0605D8499077ACA2174A7BC3 /* NSMutableAttributedString+OHAdditions.h in Headers */, 220 | 231C2348E53AFD20361397A7 /* OHAttributedStringAdditions.h in Headers */, 221 | 25EFA894FCAB729EB31F5606 /* UIFont+OHAdditions.h in Headers */, 222 | 8EA0B859EFB055F84A3E1ED4 /* UILabel+OHAdditions.h in Headers */, 223 | ); 224 | runOnlyForDeploymentPostprocessing = 0; 225 | }; 226 | /* End PBXHeadersBuildPhase section */ 227 | 228 | /* Begin PBXNativeTarget section */ 229 | 584F80B49FDB8CDD09B4C721 /* Pods */ = { 230 | isa = PBXNativeTarget; 231 | buildConfigurationList = AAC84FAA32524CC52E1136C5 /* Build configuration list for PBXNativeTarget "Pods" */; 232 | buildPhases = ( 233 | 06F09373BEDA145C566C772C /* Sources */, 234 | 974BBBE722E6CF70FEDE20AD /* Frameworks */, 235 | ); 236 | buildRules = ( 237 | ); 238 | dependencies = ( 239 | DF5CFFB55714D0980D9BA767 /* PBXTargetDependency */, 240 | ); 241 | name = Pods; 242 | productName = Pods; 243 | productReference = B0C3B97392A6644944FBC06F /* libPods.a */; 244 | productType = "com.apple.product-type.library.static"; 245 | }; 246 | B630051DB34C05310D5E7AEA /* Pods-OHAttributedStringAdditions */ = { 247 | isa = PBXNativeTarget; 248 | buildConfigurationList = 0AF7F5B14D08DEEAB0282CAA /* Build configuration list for PBXNativeTarget "Pods-OHAttributedStringAdditions" */; 249 | buildPhases = ( 250 | 381385E9228E3AFBA7A4CD44 /* Sources */, 251 | 83177EA619BC14F68D90BC0A /* Frameworks */, 252 | 291E79D89293D48C7967619C /* Headers */, 253 | ); 254 | buildRules = ( 255 | ); 256 | dependencies = ( 257 | ); 258 | name = "Pods-OHAttributedStringAdditions"; 259 | productName = "Pods-OHAttributedStringAdditions"; 260 | productReference = D837E030FA87A9157429DC40 /* libPods-OHAttributedStringAdditions.a */; 261 | productType = "com.apple.product-type.library.static"; 262 | }; 263 | /* End PBXNativeTarget section */ 264 | 265 | /* Begin PBXProject section */ 266 | 8B9C3D0F71044B5FD372CFAB /* Project object */ = { 267 | isa = PBXProject; 268 | attributes = { 269 | LastUpgradeCheck = 0510; 270 | }; 271 | buildConfigurationList = B6ECAD8F91C0E56D54A5F0D0 /* Build configuration list for PBXProject "Pods" */; 272 | compatibilityVersion = "Xcode 3.2"; 273 | developmentRegion = English; 274 | hasScannedForEncodings = 0; 275 | knownRegions = ( 276 | en, 277 | ); 278 | mainGroup = 1E67CAD93DDAA1D312FFD665; 279 | productRefGroup = 4A839CB6C06DB50EA78AF0C6 /* Products */; 280 | projectDirPath = ""; 281 | projectRoot = ""; 282 | targets = ( 283 | 584F80B49FDB8CDD09B4C721 /* Pods */, 284 | B630051DB34C05310D5E7AEA /* Pods-OHAttributedStringAdditions */, 285 | ); 286 | }; 287 | /* End PBXProject section */ 288 | 289 | /* Begin PBXSourcesBuildPhase section */ 290 | 06F09373BEDA145C566C772C /* Sources */ = { 291 | isa = PBXSourcesBuildPhase; 292 | buildActionMask = 2147483647; 293 | files = ( 294 | 354E4173B24BB8DE3237DDB1 /* Pods-dummy.m in Sources */, 295 | ); 296 | runOnlyForDeploymentPostprocessing = 0; 297 | }; 298 | 381385E9228E3AFBA7A4CD44 /* Sources */ = { 299 | isa = PBXSourcesBuildPhase; 300 | buildActionMask = 2147483647; 301 | files = ( 302 | 519F0CDD757677CDBCFAF7F4 /* NSAttributedString+OHAdditions.m in Sources */, 303 | 30F87631880DD8EFACC34667 /* NSMutableAttributedString+OHAdditions.m in Sources */, 304 | 37FCBABCC1038ED244ECCDEC /* Pods-OHAttributedStringAdditions-dummy.m in Sources */, 305 | 9AF427D6131DC57D3721268D /* UIFont+OHAdditions.m in Sources */, 306 | B9E9D0C465FB21FAB7024D1C /* UILabel+OHAdditions.m in Sources */, 307 | ); 308 | runOnlyForDeploymentPostprocessing = 0; 309 | }; 310 | /* End PBXSourcesBuildPhase section */ 311 | 312 | /* Begin PBXTargetDependency section */ 313 | DF5CFFB55714D0980D9BA767 /* PBXTargetDependency */ = { 314 | isa = PBXTargetDependency; 315 | name = "Pods-OHAttributedStringAdditions"; 316 | target = B630051DB34C05310D5E7AEA /* Pods-OHAttributedStringAdditions */; 317 | targetProxy = E039D9E7A242C7F396BE9B4E /* PBXContainerItemProxy */; 318 | }; 319 | /* End PBXTargetDependency section */ 320 | 321 | /* Begin XCBuildConfiguration section */ 322 | 22F095CD0312C2692A9EAD47 /* Debug */ = { 323 | isa = XCBuildConfiguration; 324 | baseConfigurationReference = C1CEAD0CF45B09A94354B503 /* Pods.debug.xcconfig */; 325 | buildSettings = { 326 | ENABLE_STRICT_OBJC_MSGSEND = YES; 327 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 328 | MTL_ENABLE_DEBUG_INFO = YES; 329 | OTHER_LDFLAGS = ""; 330 | OTHER_LIBTOOLFLAGS = ""; 331 | PODS_ROOT = "$(SRCROOT)"; 332 | PRODUCT_NAME = "$(TARGET_NAME)"; 333 | SDKROOT = iphoneos; 334 | SKIP_INSTALL = YES; 335 | }; 336 | name = Debug; 337 | }; 338 | 2DD2249116856B6B7491B3EE /* Debug */ = { 339 | isa = XCBuildConfiguration; 340 | buildSettings = { 341 | ALWAYS_SEARCH_USER_PATHS = NO; 342 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 343 | CLANG_CXX_LIBRARY = "libc++"; 344 | CLANG_ENABLE_MODULES = YES; 345 | CLANG_ENABLE_OBJC_ARC = YES; 346 | CLANG_WARN_BOOL_CONVERSION = YES; 347 | CLANG_WARN_CONSTANT_CONVERSION = YES; 348 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES; 349 | CLANG_WARN_EMPTY_BODY = YES; 350 | CLANG_WARN_ENUM_CONVERSION = YES; 351 | CLANG_WARN_INT_CONVERSION = YES; 352 | CLANG_WARN_OBJC_ROOT_CLASS = YES; 353 | CLANG_WARN_UNREACHABLE_CODE = YES; 354 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 355 | COPY_PHASE_STRIP = NO; 356 | GCC_C_LANGUAGE_STANDARD = gnu99; 357 | GCC_DYNAMIC_NO_PIC = NO; 358 | GCC_OPTIMIZATION_LEVEL = 0; 359 | GCC_PREPROCESSOR_DEFINITIONS = ( 360 | "DEBUG=1", 361 | "$(inherited)", 362 | ); 363 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 364 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 365 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 366 | GCC_WARN_UNDECLARED_SELECTOR = YES; 367 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 368 | GCC_WARN_UNUSED_FUNCTION = YES; 369 | GCC_WARN_UNUSED_VARIABLE = YES; 370 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 371 | ONLY_ACTIVE_ARCH = YES; 372 | STRIP_INSTALLED_PRODUCT = NO; 373 | SYMROOT = "${SRCROOT}/../build"; 374 | }; 375 | name = Debug; 376 | }; 377 | 4A7C2B5B472F220B86DB087A /* Release */ = { 378 | isa = XCBuildConfiguration; 379 | baseConfigurationReference = 8DDCD4DB15F67A9A237E9D6C /* Pods.release.xcconfig */; 380 | buildSettings = { 381 | ENABLE_STRICT_OBJC_MSGSEND = YES; 382 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 383 | MTL_ENABLE_DEBUG_INFO = NO; 384 | OTHER_LDFLAGS = ""; 385 | OTHER_LIBTOOLFLAGS = ""; 386 | PODS_ROOT = "$(SRCROOT)"; 387 | PRODUCT_NAME = "$(TARGET_NAME)"; 388 | SDKROOT = iphoneos; 389 | SKIP_INSTALL = YES; 390 | }; 391 | name = Release; 392 | }; 393 | 53238B73FE93D074B2FC82F6 /* Debug */ = { 394 | isa = XCBuildConfiguration; 395 | baseConfigurationReference = 90BEBAE820C26D9422299265 /* Pods-OHAttributedStringAdditions-Private.xcconfig */; 396 | buildSettings = { 397 | ENABLE_STRICT_OBJC_MSGSEND = YES; 398 | GCC_PREFIX_HEADER = "Target Support Files/Pods-OHAttributedStringAdditions/Pods-OHAttributedStringAdditions-prefix.pch"; 399 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 400 | MTL_ENABLE_DEBUG_INFO = YES; 401 | OTHER_LDFLAGS = ""; 402 | OTHER_LIBTOOLFLAGS = ""; 403 | PRODUCT_NAME = "$(TARGET_NAME)"; 404 | SDKROOT = iphoneos; 405 | SKIP_INSTALL = YES; 406 | }; 407 | name = Debug; 408 | }; 409 | 900296E669CAF66ED53C128F /* Release */ = { 410 | isa = XCBuildConfiguration; 411 | baseConfigurationReference = 90BEBAE820C26D9422299265 /* Pods-OHAttributedStringAdditions-Private.xcconfig */; 412 | buildSettings = { 413 | ENABLE_STRICT_OBJC_MSGSEND = YES; 414 | GCC_PREFIX_HEADER = "Target Support Files/Pods-OHAttributedStringAdditions/Pods-OHAttributedStringAdditions-prefix.pch"; 415 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 416 | MTL_ENABLE_DEBUG_INFO = NO; 417 | OTHER_LDFLAGS = ""; 418 | OTHER_LIBTOOLFLAGS = ""; 419 | PRODUCT_NAME = "$(TARGET_NAME)"; 420 | SDKROOT = iphoneos; 421 | SKIP_INSTALL = YES; 422 | }; 423 | name = Release; 424 | }; 425 | 9BA434F293314F021DBF2A4B /* Release */ = { 426 | isa = XCBuildConfiguration; 427 | buildSettings = { 428 | ALWAYS_SEARCH_USER_PATHS = NO; 429 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 430 | CLANG_CXX_LIBRARY = "libc++"; 431 | CLANG_ENABLE_MODULES = YES; 432 | CLANG_ENABLE_OBJC_ARC = YES; 433 | CLANG_WARN_BOOL_CONVERSION = YES; 434 | CLANG_WARN_CONSTANT_CONVERSION = YES; 435 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES; 436 | CLANG_WARN_EMPTY_BODY = YES; 437 | CLANG_WARN_ENUM_CONVERSION = YES; 438 | CLANG_WARN_INT_CONVERSION = YES; 439 | CLANG_WARN_OBJC_ROOT_CLASS = YES; 440 | CLANG_WARN_UNREACHABLE_CODE = YES; 441 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 442 | COPY_PHASE_STRIP = YES; 443 | ENABLE_NS_ASSERTIONS = NO; 444 | GCC_C_LANGUAGE_STANDARD = gnu99; 445 | GCC_PREPROCESSOR_DEFINITIONS = "RELEASE=1"; 446 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 447 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 448 | GCC_WARN_UNDECLARED_SELECTOR = YES; 449 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 450 | GCC_WARN_UNUSED_FUNCTION = YES; 451 | GCC_WARN_UNUSED_VARIABLE = YES; 452 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 453 | STRIP_INSTALLED_PRODUCT = NO; 454 | SYMROOT = "${SRCROOT}/../build"; 455 | VALIDATE_PRODUCT = YES; 456 | }; 457 | name = Release; 458 | }; 459 | /* End XCBuildConfiguration section */ 460 | 461 | /* Begin XCConfigurationList section */ 462 | 0AF7F5B14D08DEEAB0282CAA /* Build configuration list for PBXNativeTarget "Pods-OHAttributedStringAdditions" */ = { 463 | isa = XCConfigurationList; 464 | buildConfigurations = ( 465 | 53238B73FE93D074B2FC82F6 /* Debug */, 466 | 900296E669CAF66ED53C128F /* Release */, 467 | ); 468 | defaultConfigurationIsVisible = 0; 469 | defaultConfigurationName = Release; 470 | }; 471 | AAC84FAA32524CC52E1136C5 /* Build configuration list for PBXNativeTarget "Pods" */ = { 472 | isa = XCConfigurationList; 473 | buildConfigurations = ( 474 | 22F095CD0312C2692A9EAD47 /* Debug */, 475 | 4A7C2B5B472F220B86DB087A /* Release */, 476 | ); 477 | defaultConfigurationIsVisible = 0; 478 | defaultConfigurationName = Release; 479 | }; 480 | B6ECAD8F91C0E56D54A5F0D0 /* Build configuration list for PBXProject "Pods" */ = { 481 | isa = XCConfigurationList; 482 | buildConfigurations = ( 483 | 2DD2249116856B6B7491B3EE /* Debug */, 484 | 9BA434F293314F021DBF2A4B /* Release */, 485 | ); 486 | defaultConfigurationIsVisible = 0; 487 | defaultConfigurationName = Release; 488 | }; 489 | /* End XCConfigurationList section */ 490 | }; 491 | rootObject = 8B9C3D0F71044B5FD372CFAB /* Project object */; 492 | } 493 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-OHAttributedStringAdditions/Pods-OHAttributedStringAdditions-Private.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods-OHAttributedStringAdditions.xcconfig" 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/OHAttributedStringAdditions" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/OHAttributedStringAdditions" 4 | OTHER_LDFLAGS = -ObjC 5 | PODS_ROOT = ${SRCROOT} 6 | SKIP_INSTALL = YES -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-OHAttributedStringAdditions/Pods-OHAttributedStringAdditions-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_OHAttributedStringAdditions : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_OHAttributedStringAdditions 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-OHAttributedStringAdditions/Pods-OHAttributedStringAdditions-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #endif 4 | 5 | #import "Pods-environment.h" 6 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-OHAttributedStringAdditions/Pods-OHAttributedStringAdditions.xcconfig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AliSoftware/OHAttributedStringAdditions/15d5d01c917ab60ac0be3ece523229a3de44a866/Example/Pods/Target Support Files/Pods-OHAttributedStringAdditions/Pods-OHAttributedStringAdditions.xcconfig -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods/Pods-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## OHAttributedStringAdditions 5 | 6 | The MIT License (MIT) 7 | 8 | Copyright (c) 2014 AliSoftware 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | 28 | Generated by CocoaPods - http://cocoapods.org 29 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/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 | The MIT License (MIT) 18 | 19 | Copyright (c) 2014 AliSoftware 20 | 21 | Permission is hereby granted, free of charge, to any person obtaining a copy 22 | of this software and associated documentation files (the "Software"), to deal 23 | in the Software without restriction, including without limitation the rights 24 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 25 | copies of the Software, and to permit persons to whom the Software is 26 | furnished to do so, subject to the following conditions: 27 | 28 | The above copyright notice and this permission notice shall be included in all 29 | copies or substantial portions of the Software. 30 | 31 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 32 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 33 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 34 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 35 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 36 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 37 | SOFTWARE. 38 | 39 | Title 40 | OHAttributedStringAdditions 41 | Type 42 | PSGroupSpecifier 43 | 44 | 45 | FooterText 46 | Generated by CocoaPods - http://cocoapods.org 47 | Title 48 | 49 | Type 50 | PSGroupSpecifier 51 | 52 | 53 | StringsTable 54 | Acknowledgements 55 | Title 56 | Acknowledgements 57 | 58 | 59 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods/Pods-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods : NSObject 3 | @end 4 | @implementation PodsDummy_Pods 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/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 | // OHAttributedStringAdditions 10 | #define COCOAPODS_POD_AVAILABLE_OHAttributedStringAdditions 11 | #define COCOAPODS_VERSION_MAJOR_OHAttributedStringAdditions 1 12 | #define COCOAPODS_VERSION_MINOR_OHAttributedStringAdditions 3 13 | #define COCOAPODS_VERSION_PATCH_OHAttributedStringAdditions 0 14 | 15 | // OHAttributedStringAdditions/Base 16 | #define COCOAPODS_POD_AVAILABLE_OHAttributedStringAdditions_Base 17 | #define COCOAPODS_VERSION_MAJOR_OHAttributedStringAdditions_Base 1 18 | #define COCOAPODS_VERSION_MINOR_OHAttributedStringAdditions_Base 3 19 | #define COCOAPODS_VERSION_PATCH_OHAttributedStringAdditions_Base 0 20 | 21 | // OHAttributedStringAdditions/UILabel 22 | #define COCOAPODS_POD_AVAILABLE_OHAttributedStringAdditions_UILabel 23 | #define COCOAPODS_VERSION_MAJOR_OHAttributedStringAdditions_UILabel 1 24 | #define COCOAPODS_VERSION_MINOR_OHAttributedStringAdditions_UILabel 3 25 | #define COCOAPODS_VERSION_PATCH_OHAttributedStringAdditions_UILabel 0 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods/Pods-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 5 | 6 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 7 | > "$RESOURCES_TO_COPY" 8 | 9 | XCASSET_FILES="" 10 | 11 | install_resource() 12 | { 13 | case $1 in 14 | *.storyboard) 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\" .storyboard`.storyboardc ${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\" .storyboard`.storyboardc" "${PODS_ROOT}/$1" --sdk "${SDKROOT}" 17 | ;; 18 | *.xib) 19 | 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}" 20 | 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}" 21 | ;; 22 | *.framework) 23 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 24 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 25 | echo "rsync -av ${PODS_ROOT}/$1 ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 26 | rsync -av "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 27 | ;; 28 | *.xcdatamodel) 29 | echo "xcrun momc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1"`.mom\"" 30 | xcrun momc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodel`.mom" 31 | ;; 32 | *.xcdatamodeld) 33 | echo "xcrun momc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodeld`.momd\"" 34 | xcrun momc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodeld`.momd" 35 | ;; 36 | *.xcmappingmodel) 37 | echo "xcrun mapc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcmappingmodel`.cdm\"" 38 | xcrun mapc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcmappingmodel`.cdm" 39 | ;; 40 | *.xcassets) 41 | XCASSET_FILES="$XCASSET_FILES '$1'" 42 | ;; 43 | /*) 44 | echo "$1" 45 | echo "$1" >> "$RESOURCES_TO_COPY" 46 | ;; 47 | *) 48 | echo "${PODS_ROOT}/$1" 49 | echo "${PODS_ROOT}/$1" >> "$RESOURCES_TO_COPY" 50 | ;; 51 | esac 52 | } 53 | 54 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 55 | if [[ "${ACTION}" == "install" ]]; then 56 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 57 | fi 58 | rm -f "$RESOURCES_TO_COPY" 59 | 60 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n $XCASSET_FILES ] 61 | then 62 | case "${TARGETED_DEVICE_FAMILY}" in 63 | 1,2) 64 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 65 | ;; 66 | 1) 67 | TARGET_DEVICE_ARGS="--target-device iphone" 68 | ;; 69 | 2) 70 | TARGET_DEVICE_ARGS="--target-device ipad" 71 | ;; 72 | *) 73 | TARGET_DEVICE_ARGS="--target-device mac" 74 | ;; 75 | esac 76 | echo $XCASSET_FILES | xargs 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}" 77 | fi 78 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods/Pods.debug.xcconfig: -------------------------------------------------------------------------------- 1 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 2 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/OHAttributedStringAdditions" 3 | OTHER_CFLAGS = $(inherited) -isystem "${PODS_ROOT}/Headers/Public" -isystem "${PODS_ROOT}/Headers/Public/OHAttributedStringAdditions" 4 | OTHER_LDFLAGS = $(inherited) -ObjC -l"Pods-OHAttributedStringAdditions" 5 | OTHER_LIBTOOLFLAGS = $(OTHER_LDFLAGS) 6 | PODS_ROOT = ${SRCROOT}/Pods -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods/Pods.release.xcconfig: -------------------------------------------------------------------------------- 1 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 2 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/OHAttributedStringAdditions" 3 | OTHER_CFLAGS = $(inherited) -isystem "${PODS_ROOT}/Headers/Public" -isystem "${PODS_ROOT}/Headers/Public/OHAttributedStringAdditions" 4 | OTHER_LDFLAGS = $(inherited) -ObjC -l"Pods-OHAttributedStringAdditions" 5 | OTHER_LIBTOOLFLAGS = $(OTHER_LDFLAGS) 6 | PODS_ROOT = ${SRCROOT}/Pods -------------------------------------------------------------------------------- /Example/UnitTests/NSMutableAttributedStringTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSMutableAttributedStringTests.m 3 | // AttributedStringDemo 4 | // 5 | // Created by Olivier Halligon on 17/08/2014. 6 | // Copyright (c) 2014 AliSoftware. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import 12 | 13 | #import "OHASATestHelper.h" 14 | 15 | @interface NSMutableAttributedStringTests : XCTestCase @end 16 | 17 | @implementation NSMutableAttributedStringTests 18 | 19 | /******************************************************************************/ 20 | #pragma mark - Text Font 21 | 22 | - (void)test_setFont 23 | { 24 | NSMutableAttributedString* str = [[NSMutableAttributedString alloc] initWithString:@"Hello world"]; 25 | UIFont* font = [UIFont fontWithPostscriptName:@"Courier" size:42]; 26 | [str setFont:font]; 27 | 28 | NSSet* attr = attributesSetInString(str); 29 | NSSet* expectedAttributes = [NSSet setWithObject: @[@0,@11,@{NSFontAttributeName:font}] ]; 30 | 31 | XCTAssertEqualObjects(attr, expectedAttributes); 32 | } 33 | 34 | - (void)test_setFont_range 35 | { 36 | NSMutableAttributedString* str = [[NSMutableAttributedString alloc] initWithString:@"Hello world"]; 37 | UIFont* font = [UIFont fontWithPostscriptName:@"Courier" size:42]; 38 | [str setFont:font range:NSMakeRange(4,2)]; 39 | 40 | NSSet* attr = attributesSetInString(str); 41 | NSSet* expectedAttributes = [NSSet setWithObject: @[@4,@2,@{NSFontAttributeName:font}] ]; 42 | 43 | XCTAssertEqualObjects(attr, expectedAttributes); 44 | } 45 | 46 | /******************************************************************************/ 47 | #pragma mark - Text Color 48 | 49 | - (void)test_setTextColor 50 | { 51 | NSMutableAttributedString* str = [[NSMutableAttributedString alloc] initWithString:@"Hello world"]; 52 | UIColor* color = [UIColor purpleColor]; 53 | [str setTextColor:color]; 54 | 55 | NSSet* attr = attributesSetInString(str); 56 | NSSet* expectedAttributes = [NSSet setWithObject: @[@0,@11,@{NSForegroundColorAttributeName:color}] ]; 57 | 58 | XCTAssertEqualObjects(attr, expectedAttributes); 59 | } 60 | 61 | - (void)test_setTextColor_range 62 | { 63 | NSMutableAttributedString* str = [[NSMutableAttributedString alloc] initWithString:@"Hello world"]; 64 | UIColor* color = [UIColor purpleColor]; 65 | [str setTextColor:color range:NSMakeRange(4, 2)]; 66 | 67 | NSSet* attr = attributesSetInString(str); 68 | NSSet* expectedAttributes = [NSSet setWithObject: @[@4,@2,@{NSForegroundColorAttributeName:color}] ]; 69 | 70 | XCTAssertEqualObjects(attr, expectedAttributes); 71 | } 72 | 73 | - (void)test_setTextBackgroundColor 74 | { 75 | NSMutableAttributedString* str = [[NSMutableAttributedString alloc] initWithString:@"Hello world"]; 76 | UIColor* color = [UIColor purpleColor]; 77 | [str setTextBackgroundColor:color]; 78 | 79 | NSSet* attr = attributesSetInString(str); 80 | NSSet* expectedAttributes = [NSSet setWithObject: @[@0,@11,@{NSBackgroundColorAttributeName:color}] ]; 81 | 82 | XCTAssertEqualObjects(attr, expectedAttributes); 83 | } 84 | 85 | - (void)test_setTextBackgroundColor_range 86 | { 87 | NSMutableAttributedString* str = [[NSMutableAttributedString alloc] initWithString:@"Hello world"]; 88 | UIColor* color = [UIColor purpleColor]; 89 | [str setTextBackgroundColor:color range:NSMakeRange(4, 2)]; 90 | 91 | NSSet* attr = attributesSetInString(str); 92 | NSSet* expectedAttributes = [NSSet setWithObject: @[@4,@2,@{NSBackgroundColorAttributeName:color}] ]; 93 | 94 | XCTAssertEqualObjects(attr, expectedAttributes); 95 | } 96 | 97 | /******************************************************************************/ 98 | #pragma mark - Text Underlining 99 | 100 | - (void)test_setTextUnderlined_YES 101 | { 102 | NSMutableAttributedString* str = [[NSMutableAttributedString alloc] initWithString:@"Hello world"]; 103 | [str setTextUnderlined:YES]; 104 | 105 | NSSet* attr = attributesSetInString(str); 106 | NSSet* expectedAttributes = [NSSet setWithObject: @[@0,@11,@{NSUnderlineStyleAttributeName:@(NSUnderlineStyleSingle|NSUnderlinePatternSolid)}] ]; 107 | 108 | XCTAssertEqualObjects(attr, expectedAttributes); 109 | } 110 | 111 | - (void)test_setTextUnderlined_NO 112 | { 113 | NSMutableAttributedString* str = [[NSMutableAttributedString alloc] initWithString:@"Hello world"]; 114 | [str setTextUnderlined:NO]; 115 | 116 | NSSet* attr = attributesSetInString(str); 117 | NSSet* expectedAttributes = [NSSet setWithObject: @[@0,@11,@{NSUnderlineStyleAttributeName:@(NSUnderlineStyleNone)}] ]; 118 | 119 | XCTAssertEqualObjects(attr, expectedAttributes); 120 | } 121 | 122 | - (void)test_setTextUnderlined_range 123 | { 124 | NSMutableAttributedString* str = [[NSMutableAttributedString alloc] initWithString:@"Hello world"]; 125 | [str setTextUnderlined:YES range:NSMakeRange(4, 2)]; 126 | 127 | NSSet* attr = attributesSetInString(str); 128 | NSSet* expectedAttributes = [NSSet setWithObject: @[@4,@2,@{NSUnderlineStyleAttributeName:@(NSUnderlineStyleSingle|NSUnderlinePatternSolid)}] ]; 129 | 130 | XCTAssertEqualObjects(attr, expectedAttributes); 131 | } 132 | 133 | - (void)test_setTextUnderlineStyle_range 134 | { 135 | NSMutableAttributedString* str = [[NSMutableAttributedString alloc] initWithString:@"Hello world"]; 136 | NSUnderlineStyle style = NSUnderlinePatternDashDotDot | NSUnderlineStyleDouble; 137 | [str setTextUnderlineStyle:style range:NSMakeRange(4, 2)]; 138 | 139 | NSSet* attr = attributesSetInString(str); 140 | NSSet* expectedAttributes = [NSSet setWithObject: @[@4,@2,@{NSUnderlineStyleAttributeName:@(style)}] ]; 141 | 142 | XCTAssertEqualObjects(attr, expectedAttributes); 143 | } 144 | 145 | - (void)test_setTextUnderlineColor 146 | { 147 | NSMutableAttributedString* str = [[NSMutableAttributedString alloc] initWithString:@"Hello world"]; 148 | UIColor* color = [UIColor orangeColor]; 149 | [str setTextUnderlineColor:color]; 150 | 151 | NSSet* attr = attributesSetInString(str); 152 | NSSet* expectedAttributes = [NSSet setWithObject: @[@0,@11,@{NSUnderlineColorAttributeName:color}] ]; 153 | 154 | XCTAssertEqualObjects(attr, expectedAttributes); 155 | } 156 | 157 | - (void)test_setTextUnderlineColor_range 158 | { 159 | NSMutableAttributedString* str = [[NSMutableAttributedString alloc] initWithString:@"Hello world"]; 160 | UIColor* color = [UIColor orangeColor]; 161 | [str setTextUnderlineColor:color range:NSMakeRange(4, 2)]; 162 | 163 | NSSet* attr = attributesSetInString(str); 164 | NSSet* expectedAttributes = [NSSet setWithObject: @[@4,@2,@{NSUnderlineColorAttributeName:color}] ]; 165 | 166 | XCTAssertEqualObjects(attr, expectedAttributes); 167 | } 168 | 169 | /******************************************************************************/ 170 | #pragma mark - Text Style & Traits 171 | 172 | - (void)test_changeFontTraitsWithBlock 173 | { 174 | NSMutableAttributedString* str = [[NSMutableAttributedString alloc] initWithString:@"Hello world"]; 175 | UIFont* font1 = [UIFont fontWithPostscriptName:@"HelveticaNeue" size:42]; 176 | [str addAttribute:NSFontAttributeName value:font1 range:NSMakeRange(0, 11)]; 177 | UIFont* font2 = [UIFont fontWithPostscriptName:@"Courier" size:19]; 178 | [str addAttribute:NSFontAttributeName value:font2 range:NSMakeRange(4, 2)]; 179 | 180 | [str changeFontTraitsWithBlock:^UIFontDescriptorSymbolicTraits(UIFontDescriptorSymbolicTraits currentTraits, NSRange _) { 181 | return currentTraits | UIFontDescriptorTraitBold; 182 | }]; 183 | 184 | NSSet* attr = attributesSetInString(str); 185 | 186 | UIFont* font1Bold = [UIFont fontWithPostscriptName:@"HelveticaNeue-Bold" size:42]; 187 | UIFont* font2Bold = [UIFont fontWithPostscriptName:@"Courier-Bold" size:19]; 188 | NSSet* expectedAttributes = [NSSet setWithObjects: 189 | @[@0,@4,@{NSFontAttributeName:font1Bold}], 190 | @[@4,@2,@{NSFontAttributeName:font2Bold}], 191 | @[@6,@5,@{NSFontAttributeName:font1Bold}], 192 | nil]; 193 | 194 | XCTAssertEqualObjects(attr, expectedAttributes); 195 | } 196 | 197 | - (void)test_changeFontTraitsInRange_withBlock 198 | { 199 | NSMutableAttributedString* str = [[NSMutableAttributedString alloc] initWithString:@"Hello world"]; 200 | UIFont* font1 = [UIFont fontWithPostscriptName:@"HelveticaNeue" size:42]; 201 | [str addAttribute:NSFontAttributeName value:font1 range:NSMakeRange(0, 11)]; 202 | UIFont* font2 = [UIFont fontWithPostscriptName:@"Courier" size:19]; 203 | [str addAttribute:NSFontAttributeName value:font2 range:NSMakeRange(4, 2)]; 204 | 205 | [str changeFontTraitsInRange:NSMakeRange(5, 3) withBlock:^UIFontDescriptorSymbolicTraits(UIFontDescriptorSymbolicTraits currentTraits, NSRange _) { 206 | return currentTraits | UIFontDescriptorTraitBold; 207 | }]; 208 | 209 | NSSet* attr = attributesSetInString(str); 210 | 211 | UIFont* font1Bold = [UIFont fontWithPostscriptName:@"HelveticaNeue-Bold" size:42]; 212 | UIFont* font2Bold = [UIFont fontWithPostscriptName:@"Courier-Bold" size:19]; 213 | NSSet* expectedAttributes = [NSSet setWithObjects: 214 | @[@0,@4,@{NSFontAttributeName:font1}], 215 | @[@4,@1,@{NSFontAttributeName:font2}], 216 | @[@5,@1,@{NSFontAttributeName:font2Bold}], 217 | @[@6,@2,@{NSFontAttributeName:font1Bold}], 218 | @[@8,@3,@{NSFontAttributeName:font1}], 219 | nil]; 220 | 221 | XCTAssertEqualObjects(attr, expectedAttributes); 222 | } 223 | 224 | - (void)test_setFontBold_YES 225 | { 226 | NSMutableAttributedString* str = [[NSMutableAttributedString alloc] initWithString:@"Hello world"]; 227 | UIFont* font = [UIFont fontWithPostscriptName:@"HelveticaNeue" size:42]; 228 | [str addAttribute:NSFontAttributeName value:font range:NSMakeRange(0, 11)]; 229 | [str setFontBold:YES]; 230 | 231 | NSSet* attr = attributesSetInString(str); 232 | UIFont* fontBold = [UIFont fontWithPostscriptName:@"HelveticaNeue-Bold" size:42]; 233 | NSSet* expectedAttributes = [NSSet setWithObject: @[@0,@11,@{NSFontAttributeName:fontBold}] ]; 234 | 235 | XCTAssertEqualObjects(attr, expectedAttributes); 236 | } 237 | 238 | - (void)test_setFontBold_NO 239 | { 240 | NSMutableAttributedString* str = [[NSMutableAttributedString alloc] initWithString:@"Hello world"]; 241 | UIFont* font = [UIFont fontWithPostscriptName:@"HelveticaNeue-Bold" size:42]; 242 | [str addAttribute:NSFontAttributeName value:font range:NSMakeRange(0, 11)]; 243 | [str setFontBold:NO]; 244 | 245 | NSSet* attr = attributesSetInString(str); 246 | UIFont* fontNoBold = [UIFont fontWithPostscriptName:@"HelveticaNeue" size:42]; 247 | NSSet* expectedAttributes = [NSSet setWithObject: @[@0,@11,@{NSFontAttributeName:fontNoBold}] ]; 248 | 249 | XCTAssertEqualObjects(attr, expectedAttributes); 250 | } 251 | 252 | - (void)test_setFontBold_range_YES 253 | { 254 | NSMutableAttributedString* str = [[NSMutableAttributedString alloc] initWithString:@"Hello world"]; 255 | UIFont* font = [UIFont fontWithPostscriptName:@"HelveticaNeue" size:42]; 256 | [str addAttribute:NSFontAttributeName value:font range:NSMakeRange(0, 11)]; 257 | [str setFontBold:YES range:NSMakeRange(4, 2)]; 258 | 259 | NSSet* attr = attributesSetInString(str); 260 | UIFont* fontBold = [UIFont fontWithPostscriptName:@"HelveticaNeue-Bold" size:42]; 261 | NSSet* expectedAttributes = [NSSet setWithObjects: 262 | @[@0,@4,@{NSFontAttributeName:font}], 263 | @[@4,@2,@{NSFontAttributeName:fontBold}], 264 | @[@6,@5,@{NSFontAttributeName:font}], 265 | nil]; 266 | 267 | XCTAssertEqualObjects(attr, expectedAttributes); 268 | } 269 | 270 | - (void)test_setFontBold_range_NO 271 | { 272 | NSMutableAttributedString* str = [[NSMutableAttributedString alloc] initWithString:@"Hello world"]; 273 | UIFont* font = [UIFont fontWithPostscriptName:@"HelveticaNeue-Bold" size:42]; 274 | [str addAttribute:NSFontAttributeName value:font range:NSMakeRange(0, 11)]; 275 | [str setFontBold:NO range:NSMakeRange(4, 2)]; 276 | 277 | NSSet* attr = attributesSetInString(str); 278 | UIFont* fontNoBold = [UIFont fontWithPostscriptName:@"HelveticaNeue" size:42]; 279 | NSSet* expectedAttributes = [NSSet setWithObjects: 280 | @[@0,@4,@{NSFontAttributeName:font}], 281 | @[@4,@2,@{NSFontAttributeName:fontNoBold}], 282 | @[@6,@5,@{NSFontAttributeName:font}], 283 | nil]; 284 | 285 | XCTAssertEqualObjects(attr, expectedAttributes); 286 | } 287 | 288 | - (void)test_setFontItalics_NO 289 | { 290 | NSMutableAttributedString* str = [[NSMutableAttributedString alloc] initWithString:@"Hello world"]; 291 | UIFont* font = [UIFont fontWithPostscriptName:@"HelveticaNeue" size:42]; 292 | [str addAttribute:NSFontAttributeName value:font range:NSMakeRange(0, 11)]; 293 | [str setFontItalics:YES]; 294 | 295 | NSSet* attr = attributesSetInString(str); 296 | UIFont* fontItalic = [UIFont fontWithPostscriptName:@"HelveticaNeue-Italic" size:42]; 297 | NSSet* expectedAttributes = [NSSet setWithObject: @[@0,@11,@{NSFontAttributeName:fontItalic}] ]; 298 | 299 | XCTAssertEqualObjects(attr, expectedAttributes); 300 | } 301 | 302 | - (void)test_setFontItalics_YES 303 | { 304 | NSMutableAttributedString* str = [[NSMutableAttributedString alloc] initWithString:@"Hello world"]; 305 | UIFont* font = [UIFont fontWithPostscriptName:@"HelveticaNeue-Italic" size:42]; 306 | [str addAttribute:NSFontAttributeName value:font range:NSMakeRange(0, 11)]; 307 | [str setFontItalics:NO]; 308 | 309 | NSSet* attr = attributesSetInString(str); 310 | UIFont* fontNoItalic = [UIFont fontWithPostscriptName:@"HelveticaNeue" size:42]; 311 | NSSet* expectedAttributes = [NSSet setWithObject: @[@0,@11,@{NSFontAttributeName:fontNoItalic}] ]; 312 | 313 | XCTAssertEqualObjects(attr, expectedAttributes); 314 | } 315 | 316 | - (void)test_setFontItalics_range_YES 317 | { 318 | NSMutableAttributedString* str = [[NSMutableAttributedString alloc] initWithString:@"Hello world"]; 319 | UIFont* font = [UIFont fontWithPostscriptName:@"HelveticaNeue" size:42]; 320 | [str addAttribute:NSFontAttributeName value:font range:NSMakeRange(0, 11)]; 321 | [str setFontItalics:YES range:NSMakeRange(4, 2)]; 322 | 323 | NSSet* attr = attributesSetInString(str); 324 | UIFont* fontItalic = [UIFont fontWithPostscriptName:@"HelveticaNeue-Italic" size:42]; 325 | NSSet* expectedAttributes = [NSSet setWithObjects: 326 | @[@0,@4,@{NSFontAttributeName:font}], 327 | @[@4,@2,@{NSFontAttributeName:fontItalic}], 328 | @[@6,@5,@{NSFontAttributeName:font}], 329 | nil]; 330 | 331 | XCTAssertEqualObjects(attr, expectedAttributes); 332 | } 333 | 334 | - (void)test_setFontItalics_range_NO 335 | { 336 | NSMutableAttributedString* str = [[NSMutableAttributedString alloc] initWithString:@"Hello world"]; 337 | UIFont* font = [UIFont fontWithPostscriptName:@"HelveticaNeue-Italic" size:42]; 338 | [str addAttribute:NSFontAttributeName value:font range:NSMakeRange(0, 11)]; 339 | [str setFontItalics:NO range:NSMakeRange(4, 2)]; 340 | 341 | NSSet* attr = attributesSetInString(str); 342 | UIFont* fontNoItalic = [UIFont fontWithPostscriptName:@"HelveticaNeue" size:42]; 343 | NSSet* expectedAttributes = [NSSet setWithObjects: 344 | @[@0,@4,@{NSFontAttributeName:font}], 345 | @[@4,@2,@{NSFontAttributeName:fontNoItalic}], 346 | @[@6,@5,@{NSFontAttributeName:font}], 347 | nil]; 348 | 349 | XCTAssertEqualObjects(attr, expectedAttributes); 350 | } 351 | 352 | /******************************************************************************/ 353 | #pragma mark - Link 354 | 355 | - (void)test_setURL_range 356 | { 357 | NSMutableAttributedString* str = [[NSMutableAttributedString alloc] initWithString:@"Hello world"]; 358 | NSURL* url = [NSURL URLWithString:@"foo://bar"]; 359 | [str addAttribute:NSLinkAttributeName value:url range:NSMakeRange(4, 2)]; 360 | 361 | NSSet* attr = attributesSetInString(str); 362 | NSSet* expectedAttributes = [NSSet setWithObject:@[@4,@2,@{NSLinkAttributeName:url}]]; 363 | 364 | XCTAssertEqualObjects(attr, expectedAttributes); 365 | } 366 | 367 | /******************************************************************************/ 368 | #pragma mark - Character Spacing 369 | 370 | - (void)test_setCharacterSpacing 371 | { 372 | NSMutableAttributedString* str = [[NSMutableAttributedString alloc] initWithString:@"Hello world"]; 373 | [str setCharacterSpacing:42]; 374 | 375 | NSSet* attr = attributesSetInString(str); 376 | NSSet* expectedAttributes = [NSSet setWithObject:@[@0,@11,@{NSKernAttributeName: @42}]]; 377 | 378 | XCTAssertEqualObjects(attr, expectedAttributes); 379 | } 380 | 381 | - (void)test_setCharacterSpacing_range 382 | { 383 | NSMutableAttributedString* str = [[NSMutableAttributedString alloc] initWithString:@"Hello world"]; 384 | [str setCharacterSpacing:42 range:NSMakeRange(4, 2)]; 385 | 386 | NSSet* attr = attributesSetInString(str); 387 | NSSet* expectedAttributes = [NSSet setWithObject:@[@4,@2,@{NSKernAttributeName: @42}]]; 388 | 389 | XCTAssertEqualObjects(attr, expectedAttributes); 390 | } 391 | 392 | /******************************************************************************/ 393 | #pragma mark - Subscript and Superscript 394 | 395 | - (void)test_setBaselineOffset 396 | { 397 | NSMutableAttributedString* str = [[NSMutableAttributedString alloc] initWithString:@"Hello world"]; 398 | [str setBaselineOffset:42]; 399 | 400 | NSSet* attr = attributesSetInString(str); 401 | NSSet* expectedAttributes = [NSSet setWithObject:@[@0,@11,@{NSBaselineOffsetAttributeName:@42}]]; 402 | 403 | XCTAssertEqualObjects(attr, expectedAttributes); 404 | } 405 | 406 | - (void)test_setBaselineOffset_range 407 | { 408 | NSMutableAttributedString* str = [[NSMutableAttributedString alloc] initWithString:@"Hello world"]; 409 | [str setBaselineOffset:42 range:NSMakeRange(4, 2)]; 410 | 411 | NSSet* attr = attributesSetInString(str); 412 | NSSet* expectedAttributes = [NSSet setWithObject:@[@4,@2,@{NSBaselineOffsetAttributeName:@42}]]; 413 | 414 | XCTAssertEqualObjects(attr, expectedAttributes); 415 | } 416 | 417 | - (void)test_setSuperscriptForRange 418 | { 419 | NSMutableAttributedString* str = [[NSMutableAttributedString alloc] initWithString:@"Hello world"]; 420 | UIFont* font = [UIFont fontWithPostscriptName:@"Helvetica" size:42]; 421 | [str addAttribute:NSFontAttributeName value:font range:NSMakeRange(0, 11)]; 422 | [str setSuperscriptForRange:NSMakeRange(4, 2)]; 423 | 424 | NSSet* attr = attributesSetInString(str); 425 | NSSet* expectedAttributes = [NSSet setWithObjects: 426 | @[@0,@4,@{NSFontAttributeName:font}], 427 | @[@4,@2,@{NSFontAttributeName:font, NSBaselineOffsetAttributeName:@(21)}], 428 | @[@6,@5,@{NSFontAttributeName:font}], 429 | nil]; 430 | 431 | XCTAssertEqualObjects(attr, expectedAttributes); 432 | } 433 | 434 | - (void)test_setSubscriptForRange 435 | { 436 | NSMutableAttributedString* str = [[NSMutableAttributedString alloc] initWithString:@"Hello world"]; 437 | UIFont* font = [UIFont fontWithPostscriptName:@"Helvetica" size:42]; 438 | [str addAttribute:NSFontAttributeName value:font range:NSMakeRange(0, 11)]; 439 | [str setSubscriptForRange:NSMakeRange(4, 2)]; 440 | 441 | NSSet* attr = attributesSetInString(str); 442 | NSSet* expectedAttributes = [NSSet setWithObjects: 443 | @[@0,@4,@{NSFontAttributeName:font}], 444 | @[@4,@2,@{NSFontAttributeName:font, NSBaselineOffsetAttributeName:@(-21)}], 445 | @[@6,@5,@{NSFontAttributeName:font}], 446 | nil]; 447 | 448 | XCTAssertEqualObjects(attr, expectedAttributes); 449 | } 450 | 451 | /******************************************************************************/ 452 | #pragma mark - Paragraph Style 453 | 454 | - (void)test_setTextAlignment 455 | { 456 | NSMutableAttributedString* str = [[NSMutableAttributedString alloc] initWithString:@"Hello world"]; 457 | [str setTextAlignment:NSTextAlignmentJustified]; 458 | 459 | NSSet* attr = attributesSetInString(str); 460 | NSMutableParagraphStyle* style = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; 461 | style.alignment = NSTextAlignmentJustified; 462 | NSSet* expectedAttributes = [NSSet setWithObject:@[@0,@11,@{NSParagraphStyleAttributeName:style}]]; 463 | 464 | XCTAssertEqualObjects(attr, expectedAttributes); 465 | } 466 | 467 | - (void)test_setTextAlignment_range 468 | { 469 | NSMutableAttributedString* str = [[NSMutableAttributedString alloc] initWithString:@"Hello world"]; 470 | [str setTextAlignment:NSTextAlignmentJustified range:NSMakeRange(4, 2)]; 471 | 472 | NSSet* attr = attributesSetInString(str); 473 | NSMutableParagraphStyle* style = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; 474 | style.alignment = NSTextAlignmentJustified; 475 | NSSet* expectedAttributes = [NSSet setWithObject:@[@4,@2,@{NSParagraphStyleAttributeName:style}]]; 476 | 477 | XCTAssertEqualObjects(attr, expectedAttributes); 478 | 479 | } 480 | 481 | 482 | - (void)test_setLineBreakMode 483 | { 484 | NSMutableAttributedString* str = [[NSMutableAttributedString alloc] initWithString:@"Hello world"]; 485 | [str setLineBreakMode:NSLineBreakByTruncatingMiddle]; 486 | 487 | NSSet* attr = attributesSetInString(str); 488 | NSMutableParagraphStyle* style = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; 489 | style.lineBreakMode = NSLineBreakByTruncatingMiddle; 490 | NSSet* expectedAttributes = [NSSet setWithObject:@[@0,@11,@{NSParagraphStyleAttributeName:style}]]; 491 | 492 | XCTAssertEqualObjects(attr, expectedAttributes); 493 | } 494 | 495 | - (void)test_setLineBreakMode_range 496 | { 497 | NSMutableAttributedString* str = [[NSMutableAttributedString alloc] initWithString:@"Hello world"]; 498 | [str setLineBreakMode:NSLineBreakByTruncatingMiddle range:NSMakeRange(4, 2)]; 499 | 500 | NSSet* attr = attributesSetInString(str); 501 | NSMutableParagraphStyle* style = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; 502 | style.lineBreakMode = NSLineBreakByTruncatingMiddle; 503 | NSSet* expectedAttributes = [NSSet setWithObject:@[@4,@2,@{NSParagraphStyleAttributeName:style}]]; 504 | 505 | XCTAssertEqualObjects(attr, expectedAttributes); 506 | } 507 | 508 | - (void)test_changeParagraphStylesWithBlock 509 | { 510 | NSMutableAttributedString* str = [[NSMutableAttributedString alloc] initWithString:@"Hello world"]; 511 | NSMutableParagraphStyle* styleMiddle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; 512 | styleMiddle.firstLineHeadIndent = 42; 513 | [str addAttribute:NSParagraphStyleAttributeName value:styleMiddle range:NSMakeRange(4, 2)]; 514 | 515 | [str changeParagraphStylesWithBlock:^(NSMutableParagraphStyle *currentStyle, NSRange aRange) { 516 | currentStyle.lineSpacing = 29; 517 | }]; 518 | 519 | NSSet* attr = attributesSetInString(str); 520 | NSMutableParagraphStyle* styleOther = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; 521 | styleMiddle.lineSpacing = 29; 522 | styleOther.lineSpacing = 29; 523 | NSSet* expectedAttributes = [NSSet setWithObjects: 524 | @[@0,@4,@{NSParagraphStyleAttributeName:styleOther}], 525 | @[@4,@2,@{NSParagraphStyleAttributeName:styleMiddle}], 526 | @[@6,@5,@{NSParagraphStyleAttributeName:styleOther}], 527 | nil]; 528 | 529 | XCTAssertEqualObjects(attr, expectedAttributes); 530 | } 531 | 532 | - (void)test_changeParagraphStylesInRange_withBlock 533 | { 534 | NSMutableAttributedString* str = [[NSMutableAttributedString alloc] initWithString:@"Hello world"]; 535 | NSMutableParagraphStyle* styleMiddleOriginal = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; 536 | styleMiddleOriginal.firstLineHeadIndent = 42; 537 | [str addAttribute:NSParagraphStyleAttributeName value:styleMiddleOriginal range:NSMakeRange(4, 2)]; 538 | 539 | [str changeParagraphStylesInRange:NSMakeRange(5, 3) withBlock:^(NSMutableParagraphStyle *currentStyle, NSRange aRange) { 540 | currentStyle.lineSpacing = 29; 541 | }]; 542 | 543 | NSSet* attr = attributesSetInString(str); 544 | NSMutableParagraphStyle* styleOtherModified = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; 545 | NSMutableParagraphStyle* styleMiddleModified = [styleMiddleOriginal mutableCopy]; 546 | styleMiddleModified.lineSpacing = 29; 547 | styleOtherModified.lineSpacing = 29; 548 | NSSet* expectedAttributes = [NSSet setWithObjects: 549 | @[@4,@1,@{NSParagraphStyleAttributeName:styleMiddleOriginal}], 550 | @[@5,@1,@{NSParagraphStyleAttributeName:styleMiddleModified}], 551 | @[@6,@2,@{NSParagraphStyleAttributeName:styleOtherModified}], 552 | nil]; 553 | 554 | XCTAssertEqualObjects(attr, expectedAttributes); 555 | } 556 | 557 | - (void)test_setParagraphStyle 558 | { 559 | NSMutableAttributedString* str = [[NSMutableAttributedString alloc] initWithString:@"Hello world"]; 560 | NSMutableParagraphStyle* style = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; 561 | style.firstLineHeadIndent = 42; 562 | style.lineSpacing = 29; 563 | style.defaultTabInterval = 37; 564 | style.paragraphSpacing = 18; 565 | style.alignment = NSTextAlignmentRight; 566 | 567 | [str setParagraphStyle:style]; 568 | 569 | NSSet* attr = attributesSetInString(str); 570 | NSSet* expectedAttributes = [NSSet setWithObject:@[@0,@11,@{NSParagraphStyleAttributeName:style}]]; 571 | 572 | XCTAssertEqualObjects(attr, expectedAttributes); 573 | } 574 | 575 | - (void)test_setParagraphStyle_range 576 | { 577 | NSMutableAttributedString* str = [[NSMutableAttributedString alloc] initWithString:@"Hello world"]; 578 | NSMutableParagraphStyle* style1 = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; 579 | style1.lineSpacing = 73; 580 | style1.tailIndent = 19; 581 | [str setParagraphStyle:style1 range:NSMakeRange(3, 4)]; 582 | 583 | NSMutableParagraphStyle* style2 = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; 584 | style2.firstLineHeadIndent = 42; 585 | style2.lineSpacing = 29; 586 | style2.defaultTabInterval = 37; 587 | style2.paragraphSpacing = 18; 588 | style2.alignment = NSTextAlignmentRight; 589 | [str setParagraphStyle:style2 range:NSMakeRange(4, 2)]; 590 | 591 | NSSet* attr = attributesSetInString(str); 592 | NSSet* expectedAttributes = [NSSet setWithObjects: 593 | @[@3,@1,@{NSParagraphStyleAttributeName:style1}], 594 | @[@4,@2,@{NSParagraphStyleAttributeName:style2}], 595 | @[@6,@1,@{NSParagraphStyleAttributeName:style1}], 596 | nil]; 597 | 598 | XCTAssertEqualObjects(attr, expectedAttributes); 599 | } 600 | 601 | @end 602 | -------------------------------------------------------------------------------- /Example/UnitTests/OHASATestHelper.h: -------------------------------------------------------------------------------- 1 | // 2 | // OHASATestHelper.h 3 | // AttributedStringDemo 4 | // 5 | // Created by Olivier Halligon on 07/09/2014. 6 | // Copyright (c) 2014 AliSoftware. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface OHASATestHelper : NSObject 12 | 13 | @end 14 | 15 | NSSet* attributesSetInString(NSAttributedString* str); 16 | -------------------------------------------------------------------------------- /Example/UnitTests/OHASATestHelper.m: -------------------------------------------------------------------------------- 1 | // 2 | // OHASATestHelper.m 3 | // AttributedStringDemo 4 | // 5 | // Created by Olivier Halligon on 07/09/2014. 6 | // Copyright (c) 2014 AliSoftware. All rights reserved. 7 | // 8 | 9 | #import "OHASATestHelper.h" 10 | 11 | @implementation OHASATestHelper 12 | 13 | @end 14 | 15 | NSSet* attributesSetInString(NSAttributedString* str) 16 | { 17 | NSMutableSet* set = [NSMutableSet set]; 18 | [str enumerateAttributesInRange:NSMakeRange(0, str.length) 19 | options:0 20 | usingBlock:^(NSDictionary *attrs, NSRange range, BOOL *stop) 21 | { 22 | if (attrs.count > 0) [set addObject:@[@(range.location),@(range.length),attrs]]; 23 | }]; 24 | return [NSSet setWithSet:set]; 25 | } 26 | 27 | -------------------------------------------------------------------------------- /Example/UnitTests/UIFontTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // AttributedStringDemoTests.m 3 | // AttributedStringDemoTests 4 | // 5 | // Created by Olivier Halligon on 13/08/2014. 6 | // Copyright (c) 2014 AliSoftware. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "OHASATestHelper.h" 12 | 13 | @interface UIFontTests : XCTestCase @end 14 | 15 | @implementation UIFontTests 16 | 17 | - (void)test_fontWithFamily_size_bold_italic_01 18 | { 19 | UIFont* font = [UIFont fontWithFamily:@"Helvetica" size:42 bold:NO italic:NO]; 20 | XCTAssertEqualObjects(font.fontDescriptor.postscriptName, @"Helvetica"); 21 | XCTAssertEqual(font.pointSize, 42); 22 | } 23 | 24 | - (void)test_fontWithFamily_size_bold_italic_02 25 | { 26 | UIFont* font = [UIFont fontWithFamily:@"Helvetica" size:42 bold:NO italic:YES]; 27 | XCTAssertEqualObjects(font.fontDescriptor.postscriptName, @"Helvetica-Oblique"); 28 | XCTAssertEqual(font.pointSize, 42); 29 | } 30 | 31 | - (void)test_fontWithFamily_size_bold_italic_03 32 | { 33 | UIFont* font = [UIFont fontWithFamily:@"Helvetica" size:42 bold:YES italic:NO]; 34 | XCTAssertEqualObjects(font.fontDescriptor.postscriptName, @"Helvetica-Bold"); 35 | XCTAssertEqual(font.pointSize, 42); 36 | } 37 | 38 | - (void)test_fontWithFamily_size_bold_italic_04 39 | { 40 | UIFont* font = [UIFont fontWithFamily:@"Helvetica" size:42 bold:YES italic:YES]; 41 | XCTAssertEqualObjects(font.fontDescriptor.postscriptName, @"Helvetica-BoldOblique"); 42 | XCTAssertEqual(font.pointSize, 42); 43 | } 44 | 45 | - (void)test_fontWithFamily_size_bold_italic_05 46 | { 47 | UIFont* font = [UIFont fontWithFamily:@"Zxqvz9pmq" size:42 bold:YES italic:YES]; 48 | XCTAssertEqualObjects(font.fontDescriptor.postscriptName, @"Helvetica"); 49 | XCTAssertEqual(font.pointSize, 42); 50 | } 51 | 52 | - (void)test_fontWithFamily_size_traits 53 | { 54 | UIFontDescriptorSymbolicTraits traits = UIFontDescriptorTraitCondensed | UIFontDescriptorTraitBold; 55 | UIFont* font = [UIFont fontWithFamily:@"Helvetica Neue" size:42 traits:traits]; 56 | XCTAssertEqualObjects(font.fontDescriptor.postscriptName, @"HelveticaNeue-CondensedBold"); 57 | XCTAssertEqual(font.pointSize, 42); 58 | } 59 | 60 | - (void)test_fontWithPostscriptName_size 61 | { 62 | UIFont* font = [UIFont fontWithPostscriptName:@"HelveticaNeue-Bold" size:37]; 63 | XCTAssertEqualObjects(font.fontDescriptor.postscriptName, @"HelveticaNeue-Bold"); 64 | XCTAssertEqual(font.pointSize, 37); 65 | } 66 | 67 | - (void)test_fontWithSymbolicTraits 68 | { 69 | UIFont* baseFont = [UIFont fontWithPostscriptName:@"Helvetica Neue" size:42]; 70 | UIFont* font = [baseFont fontWithSymbolicTraits:UIFontDescriptorTraitCondensed|UIFontDescriptorTraitBold]; 71 | XCTAssertEqualObjects(font.fontDescriptor.postscriptName, @"HelveticaNeue-CondensedBold"); 72 | XCTAssertEqual(font.pointSize, 42); 73 | 74 | } 75 | 76 | - (void)test_symbolicTraits 77 | { 78 | UIFont* font = [UIFont fontWithPostscriptName:@"HelveticaNeue-CondensedBold" size:42]; 79 | XCTAssertEqual(font.symbolicTraits, UIFontDescriptorTraitCondensed|UIFontDescriptorTraitBold); 80 | } 81 | 82 | @end 83 | -------------------------------------------------------------------------------- /Example/UnitTests/UnitTests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | com.alisoftware.${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 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 AliSoftware 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /OHAttributedStringAdditions.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | 3 | s.name = "OHAttributedStringAdditions" 4 | s.version = "1.3.0" 5 | s.summary = "Categories on NSAttributedString to add a lot of very useful convenience methods." 6 | 7 | s.description = <<-DESC.gsub(/^.*\|/,'') 8 | |This pod contains categories on `NSAttributedString` to add a lot of very useful 9 | |convenience methods to manipulate attributed strings. 10 | | 11 | |The includes convenience methods to: 12 | | * set attributes on a given range 13 | | * get attributes at a given index 14 | | 15 | |Convenience methods are available for a lot of common attributes, including: 16 | | * Fonts 17 | | * Text Foreground and Background Colors 18 | | * Text Style (bold, italics, underline) 19 | | * Links (URLs) 20 | | * Baseline offset, subscript, superscript 21 | | * Text alignment, linebreak mode, character spacing 22 | | * Paragraph Style (text indent, linespacing, …) 23 | | * etc. 24 | DESC 25 | 26 | s.homepage = "https://github.com/AliSoftware/OHAttributedStringAdditions" 27 | s.license = { :type => "MIT", :file => "LICENSE" } 28 | 29 | 30 | s.author = { "Olivier Halligon" => "olivier@halligon.net" } 31 | s.social_media_url = "http://twitter.com/aligatr" 32 | 33 | s.ios.deployment_target = "7.0" 34 | # s.osx.deployment_target = "10.8" 35 | 36 | s.source = { :git => "https://github.com/AliSoftware/OHAttributedStringAdditions.git", :tag => s.version.to_s } 37 | s.requires_arc = true 38 | 39 | 40 | ### Subspecs ### 41 | 42 | s.subspec 'Base' do |sub| 43 | sub.source_files = "Source/#{s.name}.h", "Source/{NSAttributedString,NSMutableAttributedString,UIFont}+OHAdditions.{h,m}" 44 | end 45 | 46 | s.subspec 'UILabel' do |sub| 47 | sub.source_files = "Source/UILabel+OHAdditions.{h,m}" 48 | sub.dependency "#{s.name}/Base" 49 | end 50 | 51 | end 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OHAttributedStringAdditions 2 | 3 | [![Version](http://cocoapod-badges.herokuapp.com/v/OHAttributedStringAdditions/badge.png)](http://cocoadocs.org/docsets/OHAttributedStringAdditions) 4 | [![Platform](http://cocoapod-badges.herokuapp.com/p/OHAttributedStringAdditions/badge.png)](http://cocoadocs.org/docsets/OHAttributedStringAdditions) 5 | [![Build Status](https://travis-ci.org/AliSoftware/OHAttributedStringAdditions.png?branch=master)](https://travis-ci.org/AliSoftware/OHAttributedStringAdditions) 6 | 7 | This pod contains categories on `NSAttributedString` to add a lot of very useful convenience methods to manipulate attributed strings. 8 | 9 | Convenience methods include: 10 | 11 | * **font** manipulation (`setFont:range:` and `fontAtIndex:effectiveRange:`) 12 | * **text color** (`setTextColor:range:` and `textColorAtIndex:effectiveRange:`) 13 | * **background color** 14 | * **bold**, *italics* and *underline* styles 15 | * add **URLs** to your text 16 | * **paragraph styles** (**indentation**, **line spacing**, …) 17 | * baseline adjustment, **subscript**, **superscript**, … 18 | * And much more 19 | 20 | It also contains: 21 | 22 | * A category on `UIFont` to build a font given its postscript name and derive a bold/italic font from a standard one and vice-versa. 23 | * A category on `UILabel` to make it easier to detect the character at a given coordinate, which is useful to detect if the user tapped on a link (if the character as a given tapped `CGPoint` has an associated `NSURL`) and similar stuff 24 | 25 | > Note that for advanced URL detection, you should still prefer `UITextView` (configuring it with `editable=NO`) and its dedicated delegate methods instead of using `UILabel` (which does not publicly expose its `NSLayoutManager` to properly compute the exact way its characters are laid out, forcing us to recreate the TextKit objects ourselves, contrary to `UITextView`). 26 | 27 | ## Documentation 28 | 29 | The source code is fully commented and documentation is auto-generated [here](http://cocoadocs.org/docsets/OHAttributedStringAdditions). 30 | 31 | There is also some help pages [in the repository's wiki](https://github.com/AliSoftware/OHAttributedStringAdditions/wiki). 32 | 33 | ## Installation 34 | 35 | The suggested installation is via [CocoaPods](http://cocoapods.org/). Simply add the following line to your `Podfile`: 36 | 37 | ``` 38 | pod 'OHAttributedStringAdditions' 39 | ``` 40 | 41 | Then do a `pod install`. 42 | 43 | ## Example 44 | 45 | A demo project is provided in the repository. Don't hesitate to open `Example/AttributedStringDemo.xcworkspace` and play with it. 46 | 47 | If you have CocoaPods, you can even try that Sample project even if you don't have cloned the project yet, by using `pod try OHAttributedStringAdditions` in your terminal. 48 | 49 | ![Demo Capture](README.png) 50 | 51 | ## Future improvements 52 | 53 | * Improving **documentation on edge cases**, like documenting the behavior about when some attribute is not present or if we are allowed to pass nil to arguments. 54 | * Adding **support for OSX**. This should only need little adjustments, like getting rid of the `#import ` in the pch file, or replacing `UIColor` and `UIFont` classes with `NSColor` and `NSFont` (using macros to switch from one to another depending on the SDK), but that still requires some work and tests. 55 | 56 | > _Note: The original code of these categories comes from my old `OHAttributedLabel` pod, which is now deprecated as I don't have time to maintain it. As this previous implementation was based on CoreText and was not compatible (sometimes even crash) with UIKit/TextKit, I converted those categories to create this UIKit-compliant `NSAttributedString`, not related to CoreText and `OHAttributedLabel` anymore and that now work with latest versions of iOS/UIKit/TextKit._ 57 | 58 | ## Licence 59 | 60 | This component is under the MIT Licence (See the `LICENSE` file). 61 | -------------------------------------------------------------------------------- /README.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AliSoftware/OHAttributedStringAdditions/15d5d01c917ab60ac0be3ece523229a3de44a866/README.png -------------------------------------------------------------------------------- /Source/NSAttributedString+OHAdditions.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * 3 | * Copyright (c) 2010 Olivier Halligon 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 | ******************************************************************************/ 24 | 25 | 26 | #import 27 | #import 28 | 29 | /** 30 | * Convenience methods to create and manipulate `NSAttributedString` instances 31 | */ 32 | @interface NSAttributedString (OHAdditions) 33 | 34 | /******************************************************************************/ 35 | #pragma mark - Convenience Constructors 36 | 37 | /** 38 | * Build an NSAttributedString from a plain NSString 39 | * 40 | * @param string The string to build the attributed string from. May be `nil`. 41 | * 42 | * @return A new attributed string with the given string and default attributes 43 | * 44 | * @note This is a convenience method around alloc/initWithString but which 45 | * allows the parameter to be `nil`. If you pass `nil`, this method will 46 | * return an empty NSAttributedString. 47 | */ 48 | + (instancetype)attributedStringWithString:(NSString*)string; 49 | 50 | 51 | /** 52 | * Build an NSAttributedString from a format and arguments, (like `stringWithFormat:`) 53 | * 54 | * @param format The format to build the attributed string from. You can use every 55 | * placeholder that you usually use in `-[NSString stringWithFormat:]` 56 | * 57 | * @return A new attributed string with the formatted string and default attributes 58 | * 59 | * @note This is a convenience method that calls `-[NSAttributedString initWithString:]` 60 | * with its parameter build from `-[NSString stringWithFormat:]`. 61 | */ 62 | + (instancetype)attributedStringWithFormat:(NSString*)format, ... NS_FORMAT_FUNCTION(1,2); 63 | 64 | /** 65 | * Build an NSAttributedString from another NSAttributedString 66 | * 67 | * @param attrStr The attributed string to build the attributed string from. 68 | * May be `nil`. 69 | * 70 | * @return A new NSAttributedString with the same content as the original 71 | * 72 | * @note This method is generally only useful to build: 73 | * 74 | * - an NSAttributedString from an NSMutableAttributedString 75 | * - or an NSMutableAttributedString from an NSAttributedString 76 | */ 77 | + (instancetype)attributedStringWithAttributedString:(NSAttributedString*)attrStr; 78 | 79 | /** 80 | * Build an NSAttributedString from HTML text. 81 | * 82 | * The HTML import mechanism is meant for implementing something like markdown 83 | * (that is, text styles, colors, and so on), not for general HTML import. 84 | * 85 | * @param htmlString The HTML string to build the attributed string from 86 | * 87 | * @return An NSAttributedString build from the HTML markup, 88 | * or `nil` if `htmlString` cannot be parsed. 89 | * 90 | * @note **Important** 91 | * 92 | * When this method is called from a thread other than the 93 | * main thread, the call will be dispatched synchronously on the main 94 | * thread (because the HTML importer needs to be executed on the main 95 | * thread only). Therefore, you should never call it on a thread that is 96 | * itself dispatched synchrnously from the main thread, or it will lead 97 | * to a deadlock. If you are in such a case, prefer using the 98 | * +loadHTMLString:completion: method instead. 99 | */ 100 | + (instancetype)attributedStringWithHTML:(NSString*)htmlString; 101 | 102 | /** 103 | * Parse a string containing HTML markup into an NSAttributedString 104 | * 105 | * @param htmlString The HTML string to build the attributed string from 106 | * @param completion This block is called on the mainQueue upon completion 107 | * (when the HTML string has been parsed), returning asynchronously 108 | * the resulting `NSAttributedString` (or `nil` if it cannot be parsed). 109 | * 110 | * @note This method is dispatched asynchronously on the main thread (because 111 | * the HTML importer needs to be executed on the main thead only). 112 | */ 113 | + (void)loadHTMLString:(NSString*)htmlString 114 | completion:(void(^)(NSAttributedString* attrString))completion; 115 | 116 | /******************************************************************************/ 117 | #pragma mark - Size 118 | 119 | /** 120 | * Returns the size (in points) needed to draw the attributed string. 121 | * 122 | * @param maxSize The width and height constraints to apply when computing the 123 | * string’s bounding rectangle. 124 | * 125 | * @return the CGSize (width and height) required to draw the entire contents of the string. 126 | * 127 | * @note This method allows the string to be rendered in multiple lines if 128 | * needed, taking the attributedString's lineBreak mode configured in the 129 | * string's paragraph attributes into account). 130 | */ 131 | - (CGSize)sizeConstrainedToSize:(CGSize)maxSize; 132 | 133 | /******************************************************************************/ 134 | #pragma mark - Text Font 135 | 136 | /** 137 | * The default font used in an NSAttributedString when no specific font 138 | * attribute is defined. 139 | * 140 | * @return The "Helvetica" font with size 12. 141 | */ 142 | + (UIFont*)defaultFont; 143 | 144 | /** 145 | * Returns the font applied at the given character index. 146 | * 147 | * @param index The character index for which to return the font 148 | * @param aRange If non-NULL: 149 | * 150 | * - If the font attribute exists at index, upon return `aRange` 151 | * contains a range over which the font applies. 152 | * - If the font attribute does not exist at index, upon return 153 | * `aRange` contains the range over which the font attribute does 154 | * not exist. 155 | * 156 | * The range isn’t necessarily the maximum range covered by the 157 | * font attribute. 158 | * 159 | * @return The value for the font attribute of the character at index `index`, 160 | * or `nil` if there is no font attribute. 161 | */ 162 | - (UIFont*)fontAtIndex:(NSUInteger)index 163 | effectiveRange:(NSRangePointer)aRange; 164 | 165 | /** 166 | * Executes the Block for every font attribute runs in the specified range. 167 | * 168 | * If this method is sent to an instance of NSMutableAttributedString, mutation 169 | * (deletion, addition, or change) is allowed, as long as it is within the 170 | * range provided to the block; after a mutation, the enumeration continues 171 | * with the range immediately following the processed range, after the length 172 | * of the processed range is adjusted for the mutation. (The enumerator 173 | * basically assumes any change in length occurs in the specified range.) 174 | * 175 | * @param enumerationRange If non-NULL, contains the maximum range over which 176 | * the font attributes are enumerated, clipped to 177 | * enumerationRange. 178 | * @param block The Block to apply to ranges of the font attribute in the 179 | * attributed string. The Block takes three arguments: 180 | * 181 | * - `font`: The font of the enumerated text run. 182 | * - `range`: An NSRange indicating the run of the font. 183 | * - `stop`: A reference to a Boolean value. The block can set the 184 | * value to YES to stop further processing of the set. 185 | * The stop argument is an out-only argument. You should 186 | * only ever set this Boolean to YES within the Block. 187 | * @param includeUndefined If set to YES, this method will also call the block 188 | * for every range that does not have any font defined, 189 | * passing nil to the first argument of the block. 190 | */ 191 | - (void)enumerateFontsInRange:(NSRange)enumerationRange 192 | includeUndefined:(BOOL)includeUndefined 193 | usingBlock:(void (^)(UIFont* font, NSRange range, BOOL *stop))block; 194 | 195 | /******************************************************************************/ 196 | #pragma mark - Text Color 197 | 198 | /** 199 | * Returns the foreground text color applied at the given character index. 200 | * 201 | * @param index The character index for which to return the foreground color 202 | * @param aRange If non-NULL: 203 | * 204 | * - If the foreground color attribute exists at index, upon return 205 | * `aRange` contains a range over which the foreground color applies. 206 | * - If the foreground color attribute does not exist at index, upon 207 | * return `aRange` contains the range over which the foreground color 208 | * attribute does not exist. 209 | * 210 | * The range isn’t necessarily the maximum range covered by the 211 | * foreground color attribute. 212 | * 213 | * @return The value for the foreground color attribute of the character at 214 | * index `index`, or `nil` if there is no foreground color attribute. 215 | */ 216 | - (UIColor*)textColorAtIndex:(NSUInteger)index 217 | effectiveRange:(NSRangePointer)aRange; 218 | 219 | /** 220 | * Returns the background text color applied at the given character index. 221 | * 222 | * @param index The character index for which to return the background color 223 | * @param aRange If non-NULL: 224 | * 225 | * - If the background color attribute exists at index, upon return 226 | * `aRange` contains a range over which the background color applies. 227 | * - If the background color attribute does not exist at index, upon 228 | * return `aRange` contains the range over which the background color 229 | * attribute does not exist. 230 | * 231 | * The range isn’t necessarily the maximum range covered by the 232 | * background color attribute. 233 | * 234 | * @return The value for the background color attribute of the character at 235 | * index `index`, or `nil` if there is no background color attribute. 236 | */ 237 | - (UIColor*)textBackgroundColorAtIndex:(NSUInteger)index 238 | effectiveRange:(NSRangePointer)aRange; 239 | 240 | /******************************************************************************/ 241 | #pragma mark - Text Underlining 242 | 243 | /** 244 | * Returns whether the underline attribute is applied at the given character 245 | * index. 246 | * 247 | * @param index The character index for which to return the underline state 248 | * @param aRange If non-NULL: 249 | * 250 | * - If the underline attribute exists at index, upon return `aRange` 251 | * contains a range over which the underline applies (with the same 252 | * underlineStyle). 253 | * - If the underline attribute does not exist at index, upon return 254 | * `aRange` contains the range over which the underline does not 255 | * exist. 256 | * 257 | * The range isn’t necessarily the maximum range covered by the 258 | * underline attribute. 259 | * 260 | * @return YES if the character at index `index` is underlined, 261 | * NO otherwise. 262 | * 263 | * @note This is a convenience method that calls the method 264 | * -textUnderlineStyleAtIndex:effectiveRange: and returns YES if the 265 | * result is other than NSUnderlineStyleNone. 266 | */ 267 | - (BOOL)isTextUnderlinedAtIndex:(NSUInteger)index 268 | effectiveRange:(NSRangePointer)aRange; 269 | 270 | /** 271 | * Returns the underline style applied at the given character index. 272 | * 273 | * @param index The character index for which to return the underline style 274 | * @param aRange If non-NULL: 275 | * 276 | * - If the underline style attribute exists at index, upon return 277 | * `aRange` contains a range over which the underline style applies. 278 | * - If the underline style attribute does not exist at index, upon 279 | * return `aRange` contains the range over which the underline style 280 | * attribute does not exist. 281 | * 282 | * The range isn’t necessarily the maximum range covered by the 283 | * underline style attribute. 284 | * 285 | * @return The value for the underline style attribute of the character at 286 | * index `index`, or NSUnderlineStyleNone if there is no underline style 287 | * attribute. 288 | */ 289 | - (NSUnderlineStyle)textUnderlineStyleAtIndex:(NSUInteger)index 290 | effectiveRange:(NSRangePointer)aRange; 291 | 292 | /** 293 | * Returns the underline color applied at the given character index. 294 | * 295 | * @param index The character index for which to return the underline color 296 | * @param aRange If non-NULL: 297 | * 298 | * - If the underline color attribute exists at index, upon return 299 | * `aRange` contains a range over which the underline color applies. 300 | * - If the underline color attribute does not exist at index, upon 301 | * return `aRange` contains the range over which the underline color 302 | * attribute does not exist. 303 | * 304 | * The range isn’t necessarily the maximum range covered by the 305 | * underline color attribute. 306 | * 307 | * @return The value for the underline color attribute of the character at 308 | * index `index`, or `nil` if there is no underline color attribute. 309 | */ 310 | - (UIColor*)textUnderlineColorAtIndex:(NSUInteger)index 311 | effectiveRange:(NSRangePointer)aRange; 312 | 313 | /******************************************************************************/ 314 | #pragma mark - Text Style & Traits 315 | 316 | /** 317 | * Returns whether the text is bold at the given character index. 318 | * 319 | * @param index The character index for which to return the bold state 320 | * @param aRange If non-NULL: 321 | * 322 | * - If a bold font attribute exists at index, upon return `aRange` 323 | * contains a range over which the bold font applies. 324 | * - If no font attribute exists at index, or the font attribute does 325 | * not have bold traits, upon return `aRange` contains the range over 326 | * which the non-bold font attribute apply. 327 | * 328 | * The range isn’t necessarily the maximum range covered by the 329 | * bold font attribute. 330 | * 331 | * @return YES if the character at index `index` is bold, 332 | * NO otherwise. 333 | * 334 | * @note This method actually fetch the font at the given index and the uses 335 | * NSFontDescriptor to check if the font has bold symbolic traits. 336 | */ 337 | - (BOOL)isFontBoldAtIndex:(NSUInteger)index 338 | effectiveRange:(NSRangePointer)aRange; 339 | 340 | /** 341 | * Returns whether the text is in italics at the given character index. 342 | * 343 | * @param index The character index for which to return the italics state 344 | * @param aRange If non-NULL: 345 | * 346 | * - If an italics font attribute exists at index, upon return `aRange` 347 | * contains a range over which the italics font applies. 348 | * - If no font attribute exists at index, or the font attribute does 349 | * not have italics traits, upon return `aRange` contains the range 350 | * over which the non-italics font attribute apply. 351 | * 352 | * The range isn’t necessarily the maximum range covered by the 353 | * italics font attribute. 354 | * 355 | * @return YES if the character at index `index` is in italics, 356 | * NO otherwise. 357 | * 358 | * @note This method actually fetch the font at the given index and the uses 359 | * NSFontDescriptor to check if the font has italics symbolic traits. 360 | */ 361 | - (BOOL)isFontItalicsAtIndex:(NSUInteger)index 362 | effectiveRange:(NSRangePointer)aRange; 363 | 364 | /******************************************************************************/ 365 | #pragma mark - Link 366 | 367 | /** 368 | * Returns the URL attribute at the given character index. 369 | * 370 | * @param index The character index for which to return the URL 371 | * @param aRange If non-NULL: 372 | * 373 | * - If the URL attribute exists at index, upon return `aRange` 374 | * contains a range over which the URL applies. 375 | * - If the URL attribute does not exist at index, upon return `aRange` 376 | * contains the range over which the URL attribute does not exist. 377 | * 378 | * The range isn’t necessarily the maximum range covered by the 379 | * URL attribute. 380 | * 381 | * @return The value for the URL attribute of the character at index `index`, 382 | * or `nil` if there is no URL attribute. 383 | */ 384 | - (NSURL*)URLAtIndex:(NSUInteger)index effectiveRange:(NSRangePointer)aRange; 385 | 386 | /** 387 | * Executes the Block for every URL attribute runs in the specified range. 388 | * 389 | * If this method is sent to an instance of NSMutableAttributedString, mutation 390 | * (deletion, addition, or change) is allowed, as long as it is within the 391 | * range provided to the block; after a mutation, the enumeration continues 392 | * with the range immediately following the processed range, after the length 393 | * of the processed range is adjusted for the mutation. (The enumerator 394 | * basically assumes any change in length occurs in the specified range.) 395 | * 396 | * @param enumerationRange If non-NULL, contains the maximum range over which 397 | * the URL attributes are enumerated, clipped to 398 | * enumerationRange. 399 | * @param block The Block to apply to ranges of the URL attribute in the 400 | * attributed string. The Block takes three arguments: 401 | * 402 | * - `link`: The URL of the enumerated text run. 403 | * - `range`: An NSRange indicating the run of the URL. 404 | * - `stop`: A reference to a Boolean value. The block can set the 405 | * value to YES to stop further processing of the set. 406 | * The stop argument is an out-only argument. You should 407 | * only ever set this Boolean to YES within the Block. 408 | */ 409 | - (void)enumerateURLsInRange:(NSRange)enumerationRange 410 | usingBlock:(void (^)(NSURL* link, NSRange range, BOOL *stop))block; 411 | 412 | /******************************************************************************/ 413 | #pragma mark - Character Spacing 414 | 415 | /** 416 | * Returns the character spacing (kern attribute) applied at the given 417 | * character index. 418 | * 419 | * @param index The character index for which to return the character spacing 420 | * @param aRange If non-NULL: 421 | * 422 | * - If the kern attribute exists at index, upon return `aRange` 423 | * contains a range over which the kern attribute’s value applies. 424 | * - If the kern attribute does not exist at index, upon return 425 | * `aRange` contains the range over which the attribute does not 426 | * exist. 427 | * 428 | * The range isn’t necessarily the maximum range covered by the 429 | * kern attribute. 430 | * 431 | * @return The value for the kern attribute of the character at index `index`, 432 | * or 0 if there is no kern attribute (kerning disabled). 433 | * 434 | * @note For more info about typographical concepts, kerning and text layout, 435 | * see the [Text Programming Guide](https://developer.apple.com/library/ios/documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/TypoFeatures/TextSystemFeatures.html#//apple_ref/doc/uid/TP40009542-CH6-51627-BBCCHIFF) 436 | */ 437 | - (CGFloat)characterSpacingAtIndex:(NSUInteger)index effectiveRange:(NSRangePointer)aRange; 438 | 439 | /******************************************************************************/ 440 | #pragma mark - Subscript and Superscript 441 | 442 | /** 443 | * Returns the baseline offset applied at the given character index. 444 | * 445 | * @param index The character index for which to return the baseline offset 446 | * @param aRange If non-NULL: 447 | * 448 | * - If the baseline offset attribute exists at index, upon return 449 | * `aRange` contains a range over which the baseline offset applies. 450 | * - If the baseline offset attribute does not exist at index, upon 451 | * return `aRange` contains the range over which the baseline offset 452 | * attribute does not exist. 453 | * 454 | * The range isn’t necessarily the maximum range covered by the 455 | * baseline offset attribute. 456 | * 457 | * @return The value for the baseline offset of the character at index `index`, 458 | * or 0 if there is no baseline offset attribute. 459 | */ 460 | - (CGFloat)baselineOffsetAtIndex:(NSUInteger)index effectiveRange:(NSRangePointer)aRange; 461 | 462 | /******************************************************************************/ 463 | #pragma mark - Paragraph Style 464 | 465 | /** 466 | * Returns the paragraph's text alignment applied at the given character index. 467 | * 468 | * @param index The character index for which to return the text aligment 469 | * @param aRange If non-NULL: 470 | * 471 | * - If the paragraph style attribute exists at index, upon return 472 | * `aRange` contains a range over which the paragraph style's text 473 | * alignment applies. 474 | * - If the paragraph style attribute does not exist at index, upon 475 | * return `aRange` contains the range over which the paragraph style 476 | * attribute does not exist. 477 | * 478 | * The range isn’t necessarily the maximum range covered by the 479 | * paragraph style attribute's text aligmnent. 480 | * 481 | * @return The value for the paragraph style's aligment attribute at the 482 | * character index `index`, or the default NSTextAlignmentLeft if there 483 | * is no paragraph style attribute. 484 | * 485 | * @note This is a convenience method that calls 486 | * -paragraphStyleAtIndex:effectiveRange: and returns the returned 487 | * object's aligbment property. 488 | */ 489 | - (NSTextAlignment)textAlignmentAtIndex:(NSUInteger)index effectiveRange:(NSRangePointer)aRange; 490 | 491 | /** 492 | * Returns the paragraph's line break mode applied at the given character 493 | * index. 494 | * 495 | * @param index The character index for which to return the line break mode 496 | * @param aRange If non-NULL: 497 | * 498 | * - If the paragraph style attribute exists at index, upon return 499 | * `aRange` contains a range over which the paragraph style's line 500 | * break mode applies. 501 | * - If the paragraph style attribute does not exist at index, upon 502 | * return `aRange` contains the range over which the paragraph style 503 | * attribute does not exist. 504 | * 505 | * The range isn’t necessarily the maximum range covered by the 506 | * paragraph style attribute's line break mode. 507 | * 508 | * @return The value for the paragraph style's line break mode attribute at the 509 | * character index `index`, or the default NSLineBreakByWordWrapping if 510 | * there is no paragraph style attribute. 511 | * 512 | * @note This is a convenience method that calls 513 | * -paragraphStyleAtIndex:effectiveRange: and returns the returned 514 | * object's lineBreakMode property. 515 | */ 516 | - (NSLineBreakMode)lineBreakModeAtIndex:(NSUInteger)index effectiveRange:(NSRangePointer)aRange; 517 | 518 | /** 519 | * Returns the paragraph style applied at the given character index. 520 | * 521 | * @param index The character index for which to return the paragraph style 522 | * @param aRange If non-NULL: 523 | * 524 | * - If the paragraph style attribute exists at index, upon return 525 | * `aRange` contains a range over which the paragraph style applies. 526 | * - If the paragraph style attribute does not exist at index, upon 527 | * return `aRange` contains the range over which the paragraph style 528 | * attribute does not exist. 529 | * 530 | * The range isn’t necessarily the maximum range covered by the 531 | * paragraph style attribute. 532 | * 533 | * @return The value for the paragraph style attribute at the character index 534 | * `index`, or `nil` if there is no paragraph style attribute. 535 | */ 536 | - (NSParagraphStyle*)paragraphStyleAtIndex:(NSUInteger)index effectiveRange:(NSRangePointer)aRange; 537 | 538 | /** 539 | * Executes the Block for every paragraph style attribute runs in the 540 | * specified range. 541 | * 542 | * If this method is sent to an instance of NSMutableAttributedString, mutation 543 | * (deletion, addition, or change) is allowed, as long as it is within the 544 | * range provided to the block; after a mutation, the enumeration continues 545 | * with the range immediately following the processed range, after the length 546 | * of the processed range is adjusted for the mutation. (The enumerator 547 | * basically assumes any change in length occurs in the specified range.) 548 | * 549 | * @param enumerationRange If non-NULL, contains the maximum range over which 550 | * the paragraph style attributes are enumerated, 551 | * clipped to enumerationRange. 552 | * @param block The Block to apply to ranges of the paragraph style attribute 553 | * in the attributed string. The Block takes three arguments: 554 | * 555 | * - `style`: The paragraph style of the enumerated text run. 556 | * - `range`: An NSRange indicating the run of the paragraph 557 | * style. 558 | * - `stop`: A reference to a Boolean value. The block can set the 559 | * value to YES to stop further processing of the set. 560 | * The stop argument is an out-only argument. You should 561 | * only ever set this Boolean to YES within the Block. 562 | * @param includeUndefined If set to YES, this method will also call the block 563 | * for every range that does not have any paragraph style 564 | * defined, passing nil to the first argument of the block. 565 | * 566 | */ 567 | - (void)enumerateParagraphStylesInRange:(NSRange)enumerationRange 568 | includeUndefined:(BOOL)includeUndefined 569 | usingBlock:(void (^)(NSParagraphStyle* style, NSRange range, BOOL *stop))block; 570 | 571 | @end 572 | 573 | 574 | 575 | -------------------------------------------------------------------------------- /Source/NSAttributedString+OHAdditions.m: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This software is under the MIT License quoted below: 3 | ******************************************************************************* 4 | * 5 | * Copyright (c) 2010 Olivier Halligon 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | * 25 | ******************************************************************************/ 26 | 27 | 28 | #import "NSAttributedString+OHAdditions.h" 29 | 30 | @implementation NSAttributedString (OHAdditions) 31 | 32 | /******************************************************************************/ 33 | #pragma mark - Convenience Constructors 34 | 35 | + (instancetype)attributedStringWithString:(NSString*)string 36 | { 37 | if (string) 38 | { 39 | return [[self alloc] initWithString:string]; 40 | } 41 | else 42 | { 43 | return [self new]; 44 | } 45 | } 46 | 47 | + (instancetype)attributedStringWithFormat:(NSString*)format, ... 48 | { 49 | va_list args; 50 | va_start(args, format); 51 | NSString* string = [[NSString alloc] initWithFormat:format arguments:args]; 52 | va_end(args); 53 | return [[self alloc] initWithString:string]; 54 | } 55 | 56 | + (instancetype)attributedStringWithAttributedString:(NSAttributedString*)attrStr 57 | { 58 | if (attrStr) 59 | { 60 | return [[self alloc] initWithAttributedString:attrStr]; 61 | } 62 | else 63 | { 64 | return [self new]; 65 | } 66 | } 67 | 68 | + (instancetype)attributedStringWithHTML:(NSString*)htmlString 69 | { 70 | NSData* htmlData = [htmlString dataUsingEncoding:NSUTF8StringEncoding]; 71 | if (htmlData) 72 | { 73 | NSDictionary* options = @{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, 74 | NSCharacterEncodingDocumentAttribute: @(NSUTF8StringEncoding)}; 75 | __block id attributedString = nil; 76 | dispatch_block_t block = ^{ 77 | attributedString = [[self alloc] initWithData:htmlData 78 | options:options 79 | documentAttributes:nil 80 | error:NULL]; 81 | }; 82 | 83 | if ([NSThread isMainThread]) 84 | { 85 | block(); 86 | } 87 | else 88 | { 89 | // See Apple Doc: HTML importer should always be called on the main thread 90 | dispatch_sync(dispatch_get_main_queue(), block); 91 | } 92 | 93 | return attributedString; 94 | } 95 | else 96 | { 97 | return nil; 98 | } 99 | } 100 | 101 | + (void)loadHTMLString:(NSString*)htmlString 102 | completion:(void(^)(NSAttributedString* attrString))completion 103 | { 104 | if (!completion) return; 105 | 106 | NSData* htmlData = [htmlString dataUsingEncoding:NSUTF8StringEncoding]; 107 | if (htmlData) 108 | { 109 | NSDictionary* options = @{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, 110 | NSCharacterEncodingDocumentAttribute: @(NSUTF8StringEncoding)}; 111 | dispatch_async(dispatch_get_main_queue(), ^{ 112 | NSAttributedString* attributedString = [[self alloc] initWithData:htmlData 113 | options:options 114 | documentAttributes:nil 115 | error:NULL]; 116 | completion(attributedString); 117 | }); 118 | } 119 | else 120 | { 121 | dispatch_async(dispatch_get_main_queue(), ^{ 122 | completion(nil); 123 | }); 124 | } 125 | } 126 | 127 | /******************************************************************************/ 128 | #pragma mark - Size 129 | 130 | - (CGSize)sizeConstrainedToSize:(CGSize)maxSize 131 | { 132 | // Use NSStringDrawingUsesLineFragmentOrigin to compute bounds of multi-line strings (see Apple doc) 133 | CGRect bounds = [self boundingRectWithSize:maxSize 134 | options:NSStringDrawingUsesLineFragmentOrigin 135 | context:nil]; 136 | 137 | // We need to ceil the returned values (see Apple doc) 138 | return CGSizeMake((CGFloat)ceil((double)bounds.size.width), 139 | (CGFloat)ceil((double)bounds.size.height) ); 140 | } 141 | 142 | /******************************************************************************/ 143 | #pragma mark - Text Font 144 | 145 | + (UIFont*)defaultFont 146 | { 147 | // The default font used for NSAttributedString according to the doc 148 | return [UIFont fontWithName:@"Helvetica" size:12]; 149 | } 150 | 151 | - (UIFont*)fontAtIndex:(NSUInteger)index effectiveRange:(NSRangePointer)aRange 152 | { 153 | return [self attribute:NSFontAttributeName atIndex:index effectiveRange:aRange]; 154 | } 155 | 156 | - (void)enumerateFontsInRange:(NSRange)enumerationRange 157 | includeUndefined:(BOOL)includeUndefined 158 | usingBlock:(void (^)(UIFont*, NSRange, BOOL *))block 159 | { 160 | NSParameterAssert(block); 161 | 162 | [self enumerateAttribute:NSFontAttributeName 163 | inRange:enumerationRange 164 | options:0 165 | usingBlock:^(id font, NSRange aRange, BOOL *stop) 166 | { 167 | if (font || includeUndefined) block(font, aRange, stop); 168 | }]; 169 | } 170 | 171 | /******************************************************************************/ 172 | #pragma mark - Text Color 173 | 174 | - (UIColor*)textColorAtIndex:(NSUInteger)index effectiveRange:(NSRangePointer)aRange 175 | { 176 | return [self attribute:NSForegroundColorAttributeName atIndex:index effectiveRange:aRange]; 177 | } 178 | 179 | - (UIColor*)textBackgroundColorAtIndex:(NSUInteger)index effectiveRange:(NSRangePointer)aRange 180 | { 181 | return [self attribute:NSBackgroundColorAttributeName atIndex:index effectiveRange:aRange]; 182 | } 183 | 184 | /******************************************************************************/ 185 | #pragma mark - Text Underlining 186 | 187 | - (BOOL)isTextUnderlinedAtIndex:(NSUInteger)index effectiveRange:(NSRangePointer)aRange 188 | { 189 | NSUnderlineStyle underlineStyle = [self textUnderlineStyleAtIndex:index effectiveRange:aRange]; 190 | return underlineStyle != NSUnderlineStyleNone; 191 | } 192 | 193 | - (NSUnderlineStyle)textUnderlineStyleAtIndex:(NSUInteger)index effectiveRange:(NSRangePointer)aRange 194 | { 195 | NSNumber* attr = [self attribute:NSUnderlineStyleAttributeName atIndex:index effectiveRange:aRange]; 196 | return [attr integerValue]; 197 | } 198 | 199 | - (UIColor*)textUnderlineColorAtIndex:(NSUInteger)index effectiveRange:(NSRangePointer)aRange 200 | { 201 | return [self attribute:NSUnderlineColorAttributeName atIndex:index effectiveRange:aRange]; 202 | } 203 | 204 | /******************************************************************************/ 205 | #pragma mark - Text Style & Traits 206 | 207 | - (BOOL)isFontBoldAtIndex:(NSUInteger)index effectiveRange:(NSRangePointer)aRange 208 | { 209 | UIFont* font = [self fontAtIndex:index effectiveRange:aRange]; 210 | UIFontDescriptorSymbolicTraits symTraits = font.fontDescriptor.symbolicTraits; 211 | return (symTraits & UIFontDescriptorTraitBold) != 0; 212 | } 213 | 214 | - (BOOL)isFontItalicsAtIndex:(NSUInteger)index effectiveRange:(NSRangePointer)aRange 215 | { 216 | UIFont* font = [self fontAtIndex:index effectiveRange:aRange]; 217 | UIFontDescriptorSymbolicTraits symTraits = font.fontDescriptor.symbolicTraits; 218 | return (symTraits & UIFontDescriptorTraitItalic) != 0; 219 | } 220 | 221 | /******************************************************************************/ 222 | #pragma mark - Links 223 | 224 | - (NSURL*)URLAtIndex:(NSUInteger)index effectiveRange:(NSRangePointer)aRange 225 | { 226 | return [self attribute:NSLinkAttributeName atIndex:index effectiveRange:aRange]; 227 | } 228 | 229 | - (void)enumerateURLsInRange:(NSRange)enumerationRange 230 | usingBlock:(void (^)(NSURL*, NSRange, BOOL *))block 231 | { 232 | NSParameterAssert(block); 233 | 234 | [self enumerateAttribute:NSLinkAttributeName 235 | inRange:enumerationRange 236 | options:0 237 | usingBlock:^(id url, NSRange range, BOOL *stop) 238 | { 239 | if (url) block(url, range, stop); 240 | }]; 241 | } 242 | 243 | /******************************************************************************/ 244 | #pragma mark - Character Spacing 245 | 246 | - (CGFloat)characterSpacingAtIndex:(NSUInteger)index effectiveRange:(NSRangePointer)aRange 247 | { 248 | return [[self attribute:NSKernAttributeName atIndex:index effectiveRange:aRange] floatValue]; 249 | } 250 | 251 | /******************************************************************************/ 252 | #pragma mark - Subscript and Superscript 253 | 254 | - (CGFloat)baselineOffsetAtIndex:(NSUInteger)index effectiveRange:(NSRangePointer)aRange 255 | { 256 | return [[self attribute:NSBaselineOffsetAttributeName atIndex:index effectiveRange:aRange] floatValue]; 257 | } 258 | 259 | /******************************************************************************/ 260 | #pragma mark - Paragraph Style 261 | 262 | - (NSTextAlignment)textAlignmentAtIndex:(NSUInteger)index effectiveRange:(NSRangePointer)aRange 263 | { 264 | NSParagraphStyle* style = [self paragraphStyleAtIndex:index effectiveRange:aRange]; 265 | return style.alignment; 266 | } 267 | 268 | - (NSLineBreakMode)lineBreakModeAtIndex:(NSUInteger)index effectiveRange:(NSRangePointer)aRange 269 | { 270 | NSParagraphStyle* style = [self paragraphStyleAtIndex:index effectiveRange:aRange]; 271 | return style.lineBreakMode; 272 | } 273 | 274 | - (NSParagraphStyle*)paragraphStyleAtIndex:(NSUInteger)index effectiveRange:(NSRangePointer)aRange 275 | { 276 | return [self attribute:NSParagraphStyleAttributeName atIndex:index effectiveRange:aRange]; 277 | } 278 | 279 | - (void)enumerateParagraphStylesInRange:(NSRange)enumerationRange 280 | includeUndefined:(BOOL)includeUndefined 281 | usingBlock:(void (^)(NSParagraphStyle*, NSRange, BOOL *))block 282 | { 283 | NSParameterAssert(block); 284 | 285 | [self enumerateAttribute:NSParagraphStyleAttributeName 286 | inRange:enumerationRange 287 | options:0 288 | usingBlock:^(id style, NSRange aRange, BOOL *stop) 289 | { 290 | if (style || includeUndefined) block(style, aRange, stop); 291 | }]; 292 | } 293 | 294 | @end 295 | 296 | 297 | 298 | 299 | -------------------------------------------------------------------------------- /Source/NSMutableAttributedString+OHAdditions.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * 3 | * Copyright (c) 2010 Olivier Halligon 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 | ******************************************************************************/ 24 | 25 | 26 | #import 27 | #import 28 | 29 | /** 30 | * Convenience methods to modify `NSMutableAttributedString` instances 31 | */ 32 | @interface NSMutableAttributedString (OHAdditions) 33 | 34 | /******************************************************************************/ 35 | #pragma mark - Text Font 36 | 37 | /** 38 | * Set the font for the whole attributed string. 39 | * 40 | * The font of the whole attributed string will be overridden. 41 | * 42 | * @param font The font to apply 43 | * 44 | * @note You can take advantage of `UIFont+OHAdditions` category to create a 45 | * font with a given family, size and traits. 46 | */ 47 | - (void)setFont:(UIFont*)font; 48 | 49 | /** 50 | * Set the font for the given range of characters. 51 | * 52 | * @param font The font to apply 53 | * @param range The range of characters to which the font should apply. 54 | * 55 | * @note You can take advantage of `UIFont+OHAdditions` category to create a 56 | * font with a given family, size and traits. 57 | */ 58 | - (void)setFont:(UIFont*)font range:(NSRange)range; 59 | 60 | /******************************************************************************/ 61 | #pragma mark - Text Color 62 | 63 | /** 64 | * Set the foreground text color for the whole attributed string. 65 | * 66 | * @param color The foreground color to apply 67 | */ 68 | - (void)setTextColor:(UIColor*)color; 69 | 70 | /** 71 | * Set the foreground text color for the given range of characters. 72 | * 73 | * @param color The foreground color to apply 74 | * @param range The range of characters to which the color should apply. 75 | */ 76 | - (void)setTextColor:(UIColor*)color range:(NSRange)range; 77 | 78 | /** 79 | * Set the background text color for the whole attributed string. 80 | * 81 | * @param color The background color to apply 82 | */ 83 | - (void)setTextBackgroundColor:(UIColor*)color; 84 | 85 | /** 86 | * Set the background text color for the given range of characters. 87 | * 88 | * @param color The background color to apply 89 | * @param range The range of characters to which the color should apply. 90 | */ 91 | - (void)setTextBackgroundColor:(UIColor*)color range:(NSRange)range; 92 | 93 | /******************************************************************************/ 94 | #pragma mark - Text Underlining 95 | 96 | /** 97 | * Set the underline for the whole attributed string. 98 | * 99 | * @param underlined `YES` if you want the text to be underlined, `NO` otherwise. 100 | * 101 | * @note This is a convenience method that ends up calling 102 | * `setTextUnderlineStyle:range:` with the value 103 | * `(NSUnderlineStyleSingle|NSUnderlinePatternSolid)` if underlined is 104 | * `YES`, `NSUnderlineStyleNone` if underlined is `NO`. 105 | */ 106 | - (void)setTextUnderlined:(BOOL)underlined; 107 | 108 | /** 109 | * Set the underline for the given range of characters. 110 | * 111 | * @param underlined `YES` if you want the text to be underlined, `NO` otherwise. 112 | * @param range The range of characters to which the underline should apply. 113 | * 114 | * @note This is a convenience method that ends up calling 115 | * `setTextUnderlineStyle:range:` with the value 116 | * `(NSUnderlineStyleSingle|NSUnderlinePatternSolid)` if underlined is 117 | * `YES`, `NSUnderlineStyleNone` if underlined is `NO`. 118 | */ 119 | - (void)setTextUnderlined:(BOOL)underlined range:(NSRange)range; 120 | 121 | /** 122 | * Set the underline style for the given range of characters. 123 | * 124 | * @param style The underline style mask, like 125 | * `NSUnderlineStyleSingle|NSUnderlinePatternDot` or 126 | * `NSUnderlineStyleDouble|NSUnderlinePatternSolid` for example. 127 | * @param range The range of characters to which the underline style should 128 | * apply. 129 | */ 130 | - (void)setTextUnderlineStyle:(NSUnderlineStyle)style range:(NSRange)range; 131 | 132 | /** 133 | * Set the underline color for the whole attributed string 134 | * 135 | * @param color The color to apply to the underlining. If nil, the underlining 136 | * will be the same color as the text foreground color. 137 | */ 138 | - (void)setTextUnderlineColor:(UIColor*)color; 139 | 140 | /** 141 | * Set the underline color for the given range of characters. 142 | * 143 | * @param color The color to apply to the underlining. If nil, the underlining 144 | * will be the same color as the text foreground color. 145 | * 146 | * @param range The range of characters to which the underline color should 147 | * apply. 148 | */ 149 | - (void)setTextUnderlineColor:(UIColor*)color range:(NSRange)range; 150 | 151 | /******************************************************************************/ 152 | #pragma mark - Text Style & Traits 153 | 154 | /** 155 | * Enumerates on every font in the attributed string, allowing you to 156 | * easily give new font symbolic traits to each font enumerated. 157 | * 158 | * This method is useful to easily change font traits of a whole attributed 159 | * string without changing/overridding the font family of each font run. For 160 | * example, you can use this method to add or remove the 161 | * `UIFontDescriptorTraitItalic``, `UIFontDescriptorTraitBold`, 162 | * `UIFontDescriptorTraitExpanded` and/or `UIFontDescriptorTraitCondensed` 163 | * traits of every enumerated font. 164 | * 165 | * @param block A block that takes the enumerated font symbolic traits as 166 | * only parameter and returns the new symbolic font traits to 167 | * apply. 168 | * 169 | * @note This method also calls the enumeration block for ranges where no font 170 | * is explicitly defined (no NSFontAttribute set); in such case, the 171 | * traits of the [NSAttributedString defaultFont] are passed as first 172 | * parameter of the block. The font attribute is then explicitly 173 | * set (even if it did not exist before), to be able to change the font 174 | * traits for those ranges as well instead of keeping the default. 175 | */ 176 | - (void)changeFontTraitsWithBlock:(UIFontDescriptorSymbolicTraits(^)(UIFontDescriptorSymbolicTraits currentTraits, NSRange aRange))block; 177 | 178 | /** 179 | * Enumerates on every font in the given range of characters, allowing you to 180 | * easily give new font symbolic traits to each font enumerated. 181 | * 182 | * This method is useful to easily change font traits of a whole range without 183 | * changing/overridding the font family of each font run. For example, you can 184 | * use this method to add or remove the `UIFontDescriptorTraitItalic`, 185 | * `UIFontDescriptorTraitBold`, `UIFontDescriptorTraitExpanded` and/or 186 | * `UIFontDescriptorTraitCondensed` traits of every enumerated font. 187 | * 188 | * @param range The range of characters to which the traits should be 189 | * enumerated 190 | * @param block A block that takes the enumerated font symbolic traits as 191 | * only parameter and returns the new symbolic font traits to 192 | * apply. 193 | * 194 | * @note This method also calls the enumeration block for ranges where no font 195 | * is explicitly defined (no NSFontAttribute set); in such case, the 196 | * traits of the [NSAttributedString defaultFont] are passed as first 197 | * parameter of the block. The font attribute is then explicitly 198 | * set (even if it did not exist before), to be able to change the font 199 | * traits for those ranges as well instead of keeping the default. 200 | */ 201 | - (void)changeFontTraitsInRange:(NSRange)range 202 | withBlock:(UIFontDescriptorSymbolicTraits(^)(UIFontDescriptorSymbolicTraits currentTraits, NSRange aRange))block; 203 | 204 | /** 205 | * Change every fonts of the attributed string to their bold variant. 206 | * 207 | * @param isBold `YES` if you want to use bold font variants, `NO` if you want to 208 | * use non-bold font variants. 209 | * 210 | * @note This is a convenience method that calls 211 | * `changeFontTraitsInRange:withBlock:` with a block that adds (`isBold` 212 | * = `YES`) or remove (isBold = `NO`) the `UIFontDescriptorTraitBold` 213 | * trait to each enumerated font. 214 | */ 215 | - (void)setFontBold:(BOOL)isBold; 216 | 217 | /** 218 | * Change the font of the given range of characters to its bold variant. 219 | * 220 | * @param isBold `YES` if you want to use bold font variants, `NO` if you want to 221 | * use non-bold font variants. 222 | * @param range The range of characters to which the bold font should apply. 223 | * 224 | * @note This is a convenience method that calls 225 | * `changeFontTraitsInRange:withBlock:` with a block that adds (`isBold` 226 | * = `YES`) or remove (isBold = `NO`) the `UIFontDescriptorTraitBold` 227 | * trait to each enumerated font. 228 | */ 229 | - (void)setFontBold:(BOOL)isBold range:(NSRange)range; 230 | 231 | /** 232 | * Change every fonts of the attributed string to their italics variant. 233 | * 234 | * @param isItalics `YES` if you want to use italics font variants, 235 | * `NO` if you want to use non-italics font variants. 236 | * 237 | * @note This is a convenience method that calls 238 | * `changeFontTraitsInRange:withBlock:` with a block that adds ( 239 | * `isItalics` = `YES`) or remove (isItalics = `NO`) the 240 | * `UIFontDescriptorTraitItalic` trait to each enumerated font. 241 | */ 242 | - (void)setFontItalics:(BOOL)isItalics; 243 | 244 | /** 245 | * Change the font of the given range of characters to its italics variant. 246 | * 247 | * @param isItalics `YES` if you want to use italics font variants, 248 | * `NO` if you want to use non-italics font variants. 249 | * @param range The range of characters to which the italics font should apply. 250 | * 251 | * @note This is a convenience method that calls 252 | * changeFontTraitsInRange:withBlock: with a block that adds (isItalics = 253 | * `YES`) or remove (`isItalics` = `NO`) the 254 | * `UIFontDescriptorTraitItalic` trait to each enumerated font. 255 | */ 256 | - (void)setFontItalics:(BOOL)isItalics range:(NSRange)range; 257 | 258 | /******************************************************************************/ 259 | #pragma mark - Link 260 | 261 | /** 262 | * Add an URL link to the given range of characters. 263 | * 264 | * @param linkURL the URL to apply 265 | * @param range The range of characters to which the URL should apply. 266 | */ 267 | - (void)setURL:(NSURL*)linkURL range:(NSRange)range; 268 | 269 | /******************************************************************************/ 270 | #pragma mark - Character Spacing 271 | 272 | /** 273 | * Set the character spacing (kern attribute) of the whole attributed string 274 | * 275 | * @param characterSpacing The character spacing. This value specifies the 276 | * number of points by which to adjust kern-pair 277 | * characters. A value of 0 disables kerning. 278 | * 279 | * @note For more info about typographical concepts, kerning and text layout, 280 | * see the [Text Programming Guide](https://developer.apple.com/library/ios/documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/TypoFeatures/TextSystemFeatures.html#//apple_ref/doc/uid/TP40009542-CH6-51627-BBCCHIFF) 281 | */ 282 | - (void)setCharacterSpacing:(CGFloat)characterSpacing; 283 | 284 | /** 285 | * Set the character spacing (kern attribute) of the given range of characters. 286 | * 287 | * @param characterSpacing The character spacing. This value specifies the 288 | * number of points by which to adjust kern-pair 289 | * characters. A value of 0 disables kerning. 290 | * @param range The range of characters to which the character spacing 291 | * (kerning) should apply. 292 | * 293 | * @note For more info about typographical concepts, kerning and text layout, 294 | * see the [Text Programming Guide](https://developer.apple.com/library/ios/documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/TypoFeatures/TextSystemFeatures.html#//apple_ref/doc/uid/TP40009542-CH6-51627-BBCCHIFF) 295 | */ 296 | - (void)setCharacterSpacing:(CGFloat)characterSpacing range:(NSRange)range; 297 | 298 | /******************************************************************************/ 299 | #pragma mark - Subscript and Superscript 300 | 301 | /** 302 | * Set the baseline offset of the whole attributed string. 303 | * 304 | * @param offset The character's offset from the baseline, in points. 305 | */ 306 | - (void)setBaselineOffset:(CGFloat)offset; 307 | 308 | /** 309 | * Set the baseline offset of the given range of characters. 310 | * 311 | * @param offset The character's offset from the baseline, in points. 312 | * @param range The range of characters to which the baseline offset should 313 | * apply. 314 | */ 315 | - (void)setBaselineOffset:(CGFloat)offset range:(NSRange)range; 316 | 317 | /** 318 | * Set the given range of characters to be superscripted, meaning that 319 | * they have a positive offset above the baseline and have a font half the 320 | * current font size. 321 | * 322 | * @param range The range of characters to which the superscript should apply. 323 | * 324 | * @note This is a convenient method that calls -setBaselineOffset:range: 325 | * using a positive offset of half of the pointSize of the font at the 326 | * range's start location 327 | */ 328 | - (void)setSuperscriptForRange:(NSRange)range; 329 | 330 | /** 331 | * Set the given range of characters to be subscripted, meaning that 332 | * they have a negative offset below the baseline and have a font half the 333 | * current font size. 334 | * 335 | * @param range The range of characters to which the subscript should apply. 336 | * 337 | * @note This is a convenient method that calls -setBaselineOffset:range: 338 | * using a negative offset of half of the pointSize of the font at the 339 | * range's start location 340 | */ 341 | - (void)setSubscriptForRange:(NSRange)range; 342 | 343 | /******************************************************************************/ 344 | #pragma mark - Paragraph Style 345 | 346 | /** 347 | * Set the paragraph's text alignment for the whole attributed string. 348 | * 349 | * @param alignment The paragraph's text alignment to apply 350 | */ 351 | - (void)setTextAlignment:(NSTextAlignment)alignment; 352 | 353 | /** 354 | * Set the paragraph's text alignment for the given range of characters. 355 | * 356 | * @param alignment The paragraph's text alignment to apply 357 | * @param range The range of characters to which the text alignment should 358 | * apply. 359 | */ 360 | - (void)setTextAlignment:(NSTextAlignment)alignment range:(NSRange)range; 361 | 362 | /** 363 | * Set the paragraph's line break mode for the whole attributed string. 364 | * 365 | * @param lineBreakMode The paragraph's line break mode to apply 366 | */ 367 | - (void)setLineBreakMode:(NSLineBreakMode)lineBreakMode; 368 | 369 | /** 370 | * Set the paragraph's line break mode for the given range of characters. 371 | * 372 | * @param lineBreakMode The paragraph's line break mode to apply 373 | * @param range The range of characters to which the line break mode should 374 | * apply. 375 | */ 376 | - (void)setLineBreakMode:(NSLineBreakMode)lineBreakMode range:(NSRange)range; 377 | 378 | /** 379 | * This methods allows you to change only certain Paragraph Styles properties 380 | * without changing the others (for example changing the firstLineHeadIndent 381 | * or line spacing without overriding the textAlignment). 382 | * 383 | * It is useful as NSParagraphStyle objects carry multiple text styles in only 384 | * one common attribute, but you sometimes want to change only one of the text 385 | * style without altering the others. 386 | * 387 | * @param block A block that takes the mutable paragraph style as the only 388 | * parameter, allowing you to change its properties. 389 | * 390 | * @note This method actually enumerate all paragraph styles of your 391 | * attributed string, create a mutable copy of the NSParagraphStyle to 392 | * pass it to the block, then reapply the modified paragraph style to 393 | * the original range. 394 | * 395 | * @note This method also calls the enumeration block for ranges where no 396 | * paragraph style is explicitly defined (no NSParagraphStyleAttribute 397 | * set); in such case, the [NSParagraphStyle defaultStyle] is passed as 398 | * first parameter of the block. The paragraph style attribute is then 399 | * explicitly set (even if it did not exist before), to be able to change 400 | * the paragraph style for those ranges as well instead of keeping the default 401 | */ 402 | - (void)changeParagraphStylesWithBlock:(void(^)(NSMutableParagraphStyle* currentStyle, NSRange aRange))block; 403 | 404 | /** 405 | * This methods allows you to change only certain Paragraph Styles properties 406 | * without changing the others (for example changing the firstLineHeadIndent 407 | * or line spacing without overriding the textAlignment). 408 | * 409 | * It is useful as NSParagraphStyle objects carry multiple text styles in only 410 | * one common attribute, but you sometimes want to change only one of the text 411 | * style without altering the others. 412 | * 413 | * @param range The range of characters to which the paragraph style 414 | * modifications should apply. 415 | * @param block A block that takes the mutable paragraph style as the only 416 | * parameter, allowing you to change its properties. 417 | * 418 | * @note This method actually enumerate all paragraph styles in the given 419 | * range of your attributed string, create a mutable copy of the 420 | * NSParagraphStyle to pass it to the block, then reapply the modified 421 | * paragraph style to the original range. 422 | * 423 | * @note This method also calls the enumeration block for ranges where no 424 | * paragraph style is explicitly defined (no NSParagraphStyleAttribute 425 | * set); in such case, the [NSParagraphStyle defaultStyle] is passed as 426 | * first parameter of the block. The paragraph style attribute is then 427 | * explicitly set (even if it did not exist before), to be able to change 428 | * the paragraph style for those ranges as well instead of keeping the default 429 | */ 430 | - (void)changeParagraphStylesInRange:(NSRange)range 431 | withBlock:(void(^)(NSMutableParagraphStyle* currentStyle, NSRange aRange))block; 432 | 433 | /** 434 | * Override the Paragraph Styles, dropping the ones previously set if any. 435 | * Be aware that this will override the text alignment, linebreakmode, and all 436 | * other paragraph styles with the new values. 437 | * 438 | * @param style The new paragraph style to apply 439 | */ 440 | - (void)setParagraphStyle:(NSParagraphStyle *)style; 441 | 442 | /** 443 | * Override the Paragraph Styles of the given range of characters, dropping 444 | * the ones previously set if any. 445 | * Be aware that this will override the text alignment, linebreakmode, and all 446 | * other paragraph styles in the given range with the new values. 447 | * 448 | * @param style The new paragraph style to apply 449 | * @param range The range of characters to which the paragraphe style should 450 | * apply. 451 | */ 452 | - (void)setParagraphStyle:(NSParagraphStyle*)style range:(NSRange)range; 453 | 454 | @end 455 | -------------------------------------------------------------------------------- /Source/NSMutableAttributedString+OHAdditions.m: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This software is under the MIT License quoted below: 3 | ******************************************************************************* 4 | * 5 | * Copyright (c) 2010 Olivier Halligon 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | * 25 | ******************************************************************************/ 26 | 27 | 28 | #import "NSMutableAttributedString+OHAdditions.h" 29 | #import "NSAttributedString+OHAdditions.h" 30 | #import "UIFont+OHAdditions.h" 31 | 32 | @implementation NSMutableAttributedString (OHAdditions) 33 | 34 | /******************************************************************************/ 35 | #pragma mark - Text Font 36 | 37 | - (void)setFont:(UIFont*)font 38 | { 39 | [self setFont:font range:NSMakeRange(0, self.length)]; 40 | } 41 | 42 | - (void)setFont:(UIFont*)font range:(NSRange)range 43 | { 44 | if (font) 45 | { 46 | [self removeAttribute:NSFontAttributeName range:range]; // Work around for Apple leak 47 | [self addAttribute:NSFontAttributeName value:(id)font range:range]; 48 | } 49 | } 50 | 51 | 52 | /******************************************************************************/ 53 | #pragma mark - Text Color 54 | 55 | - (void)setTextColor:(UIColor*)color 56 | { 57 | [self setTextColor:color range:NSMakeRange(0,self.length)]; 58 | } 59 | 60 | - (void)setTextColor:(UIColor*)color range:(NSRange)range 61 | { 62 | [self removeAttribute:NSForegroundColorAttributeName range:range]; // Work around for Apple leak 63 | [self addAttribute:NSForegroundColorAttributeName value:(id)color range:range]; 64 | } 65 | 66 | - (void)setTextBackgroundColor:(UIColor*)color 67 | { 68 | [self setTextBackgroundColor:color range:NSMakeRange(0, self.length)]; 69 | } 70 | 71 | - (void)setTextBackgroundColor:(UIColor*)color range:(NSRange)range 72 | { 73 | [self removeAttribute:NSBackgroundColorAttributeName range:range]; // Work around for Apple leak 74 | [self addAttribute:NSBackgroundColorAttributeName value:(id)color range:range]; 75 | } 76 | 77 | /******************************************************************************/ 78 | #pragma mark - Text Underlining 79 | 80 | - (void)setTextUnderlined:(BOOL)underlined 81 | { 82 | [self setTextUnderlined:underlined range:NSMakeRange(0,self.length)]; 83 | } 84 | 85 | - (void)setTextUnderlined:(BOOL)underlined range:(NSRange)range 86 | { 87 | NSUnderlineStyle style = underlined ? (NSUnderlineStyleSingle|NSUnderlinePatternSolid) : NSUnderlineStyleNone; 88 | [self setTextUnderlineStyle:style range:range]; 89 | } 90 | 91 | - (void)setTextUnderlineStyle:(NSUnderlineStyle)style range:(NSRange)range 92 | { 93 | [self removeAttribute:NSUnderlineStyleAttributeName range:range]; // Work around for Apple leak 94 | [self addAttribute:NSUnderlineStyleAttributeName value:@(style) range:range]; 95 | } 96 | 97 | - (void)setTextUnderlineColor:(UIColor*)color 98 | { 99 | [self setTextUnderlineColor:color range:NSMakeRange(0, self.length)]; 100 | } 101 | 102 | - (void)setTextUnderlineColor:(UIColor*)color range:(NSRange)range 103 | { 104 | [self removeAttribute:NSUnderlineColorAttributeName range:range]; // Work around for Apple leak 105 | [self addAttribute:NSUnderlineColorAttributeName value:color range:range]; 106 | } 107 | 108 | /******************************************************************************/ 109 | #pragma mark - Text Style & Traits 110 | 111 | - (void)changeFontTraitsWithBlock:(UIFontDescriptorSymbolicTraits(^)(UIFontDescriptorSymbolicTraits, NSRange))block 112 | { 113 | [self changeFontTraitsInRange:NSMakeRange(0, self.length) 114 | withBlock:block]; 115 | } 116 | 117 | - (void)changeFontTraitsInRange:(NSRange)range 118 | withBlock:(UIFontDescriptorSymbolicTraits(^)(UIFontDescriptorSymbolicTraits, NSRange))block 119 | { 120 | NSParameterAssert(block); 121 | [self beginEditing]; 122 | [self enumerateFontsInRange:range 123 | includeUndefined:YES 124 | usingBlock:^(UIFont* font, NSRange aRange, BOOL *stop) 125 | { 126 | if (!font) font = [[self class] defaultFont]; 127 | UIFontDescriptorSymbolicTraits currentTraits = font.symbolicTraits; 128 | UIFontDescriptorSymbolicTraits newTraits = block(currentTraits, aRange); 129 | UIFont* newFont = [font fontWithSymbolicTraits:newTraits]; 130 | [self setFont:newFont range:aRange]; 131 | }]; 132 | [self endEditing]; 133 | } 134 | 135 | - (void)setFontBold:(BOOL)isBold 136 | { 137 | [self setFontBold:isBold range:NSMakeRange(0, self.length)]; 138 | } 139 | 140 | // TODO: Check out the effect of the NSStrokeWidthAttributeName attribute 141 | // to see if we can also fake bold fonts by increasing the font weight 142 | - (void)setFontBold:(BOOL)isBold range:(NSRange)range 143 | { 144 | [self changeFontTraitsInRange:range withBlock:^UIFontDescriptorSymbolicTraits(UIFontDescriptorSymbolicTraits currentTraits, NSRange enumeratedRange) 145 | { 146 | UIFontDescriptorSymbolicTraits flag = UIFontDescriptorTraitBold; 147 | return isBold ? currentTraits | flag : currentTraits & ~flag; 148 | }]; 149 | } 150 | 151 | - (void)setFontItalics:(BOOL)isItalics 152 | { 153 | [self setFontItalics:isItalics range:NSMakeRange(0, self.length)]; 154 | } 155 | 156 | // TODO: Check out the effect of the NSObliquenessAttributeName attribute 157 | // to see if we can also fake italics fonts by increasing the font skew 158 | - (void)setFontItalics:(BOOL)isItalics range:(NSRange)range 159 | { 160 | [self changeFontTraitsInRange:range withBlock:^UIFontDescriptorSymbolicTraits(UIFontDescriptorSymbolicTraits currentTraits, NSRange enumeratedRange) 161 | { 162 | UIFontDescriptorSymbolicTraits flag = UIFontDescriptorTraitItalic; 163 | return isItalics ? currentTraits | flag : currentTraits & ~flag; 164 | }]; 165 | } 166 | 167 | /******************************************************************************/ 168 | #pragma mark - Link 169 | 170 | // TODO: Check if setting an URL also changes the text color and underline 171 | // attributes. I don't believe it does, but either way, document it. 172 | - (void)setURL:(NSURL*)linkURL range:(NSRange)range 173 | { 174 | [self removeAttribute:NSLinkAttributeName range:range]; // Work around for Apple leak 175 | if (linkURL) 176 | { 177 | [self addAttribute:NSLinkAttributeName value:(id)linkURL range:range]; 178 | } 179 | } 180 | 181 | /******************************************************************************/ 182 | #pragma mark - Character Spacing 183 | 184 | - (void)setCharacterSpacing:(CGFloat)chracterSpacing 185 | { 186 | [self setCharacterSpacing:chracterSpacing range:NSMakeRange(0,self.length)]; 187 | } 188 | - (void)setCharacterSpacing:(CGFloat)characterSpacing range:(NSRange)range 189 | { 190 | [self addAttribute:NSKernAttributeName value:@(characterSpacing) range:range]; 191 | } 192 | 193 | /******************************************************************************/ 194 | #pragma mark - Subscript and Superscript 195 | 196 | - (void)setBaselineOffset:(CGFloat)offset 197 | { 198 | [self setBaselineOffset:offset range:NSMakeRange(0, self.length)]; 199 | } 200 | 201 | - (void)setBaselineOffset:(CGFloat)offset range:(NSRange)range 202 | { 203 | [self removeAttribute:NSBaselineOffsetAttributeName range:range]; // Work around for Apple leak 204 | [self addAttribute:NSBaselineOffsetAttributeName value:@(offset) range:range]; 205 | } 206 | 207 | - (void)setSuperscriptForRange:(NSRange)range 208 | { 209 | UIFont* font = [self attribute:NSFontAttributeName atIndex:range.location effectiveRange:NULL]; 210 | CGFloat offset = + font.pointSize / 2; 211 | [self setBaselineOffset:offset range:range]; 212 | } 213 | 214 | - (void)setSubscriptForRange:(NSRange)range 215 | { 216 | UIFont* font = [self attribute:NSFontAttributeName atIndex:range.location effectiveRange:NULL]; 217 | CGFloat offset = - font.pointSize / 2; 218 | [self setBaselineOffset:offset range:range]; 219 | } 220 | 221 | /******************************************************************************/ 222 | #pragma mark - Paragraph Style 223 | 224 | - (void)setTextAlignment:(NSTextAlignment)alignment 225 | { 226 | [self setTextAlignment:alignment range:NSMakeRange(0,self.length)]; 227 | } 228 | 229 | - (void)setTextAlignment:(NSTextAlignment)alignment range:(NSRange)range 230 | { 231 | [self changeParagraphStylesInRange:range withBlock:^(NSMutableParagraphStyle *paragraphStyle, NSRange enumeratedRange) { 232 | paragraphStyle.alignment = alignment; 233 | }]; 234 | } 235 | 236 | - (void)setLineBreakMode:(NSLineBreakMode)lineBreakMode 237 | { 238 | [self setLineBreakMode:lineBreakMode range:NSMakeRange(0,self.length)]; 239 | } 240 | 241 | - (void)setLineBreakMode:(NSLineBreakMode)lineBreakMode range:(NSRange)range 242 | { 243 | [self changeParagraphStylesInRange:range withBlock:^(NSMutableParagraphStyle *paragraphStyle, NSRange enumeratedRange) { 244 | paragraphStyle.lineBreakMode = lineBreakMode; 245 | }]; 246 | } 247 | 248 | 249 | - (void)changeParagraphStylesWithBlock:(void(^)(NSMutableParagraphStyle*, NSRange))block 250 | { 251 | [self changeParagraphStylesInRange:NSMakeRange(0,self.length) withBlock:block]; 252 | } 253 | 254 | - (void)changeParagraphStylesInRange:(NSRange)range withBlock:(void(^)(NSMutableParagraphStyle*, NSRange))block 255 | { 256 | NSParameterAssert(block != nil); 257 | [self beginEditing]; 258 | [self enumerateParagraphStylesInRange:range 259 | includeUndefined:YES 260 | usingBlock:^(id style, NSRange aRange, BOOL *stop) 261 | { 262 | if (!style) style = [NSParagraphStyle defaultParagraphStyle]; 263 | NSMutableParagraphStyle* newStyle = [style mutableCopy]; 264 | block(newStyle, aRange); 265 | [self setParagraphStyle:newStyle range:aRange]; 266 | }]; 267 | [self endEditing]; 268 | } 269 | 270 | - (void)setParagraphStyle:(NSParagraphStyle *)style 271 | { 272 | [self setParagraphStyle:style range:NSMakeRange(0,self.length)]; 273 | } 274 | 275 | // TODO: Check the behavior when applying a paragraph style only to some 276 | // characters in the middle of an actual paragraph. 277 | - (void)setParagraphStyle:(NSParagraphStyle*)style range:(NSRange)range 278 | { 279 | [self removeAttribute:NSParagraphStyleAttributeName range:range]; // Work around for Apple leak 280 | [self addAttribute:NSParagraphStyleAttributeName value:(id)style range:range]; 281 | } 282 | 283 | @end 284 | -------------------------------------------------------------------------------- /Source/OHAttributedStringAdditions.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This software is under the MIT License quoted below: 3 | ******************************************************************************* 4 | * 5 | * Copyright (c) 2010 Olivier Halligon 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | * 25 | ******************************************************************************/ 26 | 27 | #import "NSAttributedString+OHAdditions.h" 28 | #import "NSMutableAttributedString+OHAdditions.h" 29 | #import "UIFont+OHAdditions.h" 30 | 31 | #if __has_include("UILabel+OHAdditions.h") 32 | #import "UILabel+OHAdditions.h" 33 | #endif 34 | -------------------------------------------------------------------------------- /Source/UIFont+OHAdditions.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * 3 | * Copyright (c) 2010 Olivier Halligon 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 | ******************************************************************************/ 24 | 25 | 26 | #import 27 | 28 | /** 29 | * Convenience methods to create new `UIFont` instances and query font traits 30 | */ 31 | @interface UIFont (OHAdditions) 32 | 33 | /** 34 | * Returns a font given a family name, size, and whether it should be bold 35 | * and/or italic. 36 | * 37 | * If the font family is not found, this will return the "Helvetica" font with 38 | * the requested size, but neither bold nor italics (This behavior is 39 | * inherited by NSFontDescriptor's own native behavior) 40 | * 41 | * @param fontFamily The name of the font family, like "Helvetica" 42 | * @param size The size of the font to return 43 | * @param isBold YES if you need the bold variant of the font 44 | * @param isItalic YES if you need the italic variant of the font 45 | * 46 | * @return The corresponding UIFont. 47 | * 48 | * @note This is a convenience method that calls +fontWithFamily:size:traits: 49 | * with the traits corresponding to the bold and/or italic flags. 50 | */ 51 | + (instancetype)fontWithFamily:(NSString*)fontFamily 52 | size:(CGFloat)size 53 | bold:(BOOL)isBold 54 | italic:(BOOL)isItalic; 55 | 56 | /** 57 | * Returns a font given a family name, size and symbolic traits. 58 | * 59 | * @param fontFamily The name of the font family, like "Helvetica" 60 | * @param size The size of the font to return 61 | * @param symTraits A mask of symbolic traits, combined as an bitwise-OR mask. 62 | * e.g. UIFontDescriptorTraitBold|UIFontDescriptorTraitCondensed 63 | * 64 | * @return The corresponding UIFont. 65 | */ 66 | + (instancetype)fontWithFamily:(NSString*)fontFamily 67 | size:(CGFloat)size 68 | traits:(UIFontDescriptorSymbolicTraits)symTraits; 69 | 70 | /** 71 | * Returns a font given its Postscript Name and size 72 | * 73 | * @note Font PostScript names are unlikely to change across OS versions 74 | * (whereas it seems like common, display names may change) 75 | * 76 | * @param postscriptName The PostScript name of the font 77 | * @param size The size of the font 78 | * 79 | * @return The UIFont object with the specified PS name and size. 80 | */ 81 | + (instancetype)fontWithPostscriptName:(NSString*)postscriptName 82 | size:(CGFloat)size; 83 | 84 | /** 85 | * Returns a font with the same family name, size and attributs as the 86 | * receiver, but with the new symbolic traits replacing the current ones 87 | * 88 | * @param symTraits The new symbolic traits to use for the derived font 89 | * 90 | * @return The UIFont variant corresponding to the receiver but with the 91 | * new symbolic traits 92 | */ 93 | - (instancetype)fontWithSymbolicTraits:(UIFontDescriptorSymbolicTraits)symTraits; 94 | 95 | /** 96 | * Returns the symbolic traits of the font, telling if its bold, italics, etc. 97 | * 98 | * @return The font symbolic traits, as returned by its font descriptor. 99 | * 100 | * @note This is a convenience method that simply calls 101 | * fontDescriptor.symbolicTraits on the receiver. 102 | */ 103 | - (UIFontDescriptorSymbolicTraits)symbolicTraits; 104 | 105 | @end 106 | -------------------------------------------------------------------------------- /Source/UIFont+OHAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIFont+OHAdditions.m 3 | // AttributedStringDemo 4 | // 5 | // Created by Olivier Halligon on 16/08/2014. 6 | // Copyright (c) 2014 AliSoftware. All rights reserved. 7 | // 8 | 9 | #import "UIFont+OHAdditions.h" 10 | 11 | @implementation UIFont (OHAdditions) 12 | 13 | + (instancetype)fontWithFamily:(NSString*)fontFamily 14 | size:(CGFloat)size 15 | bold:(BOOL)isBold 16 | italic:(BOOL)isItalic 17 | { 18 | UIFontDescriptorSymbolicTraits traits = 0; 19 | if (isBold) traits |= UIFontDescriptorTraitBold; 20 | if (isItalic) traits |= UIFontDescriptorTraitItalic; 21 | 22 | return [self fontWithFamily:fontFamily size:size traits:traits]; 23 | } 24 | 25 | + (instancetype)fontWithFamily:(NSString*)fontFamily 26 | size:(CGFloat)size 27 | traits:(UIFontDescriptorSymbolicTraits)symTraits 28 | { 29 | NSDictionary* attributes = @{ UIFontDescriptorFamilyAttribute: fontFamily, 30 | UIFontDescriptorTraitsAttribute: @{UIFontSymbolicTrait:@(symTraits)}}; 31 | 32 | UIFontDescriptor* desc = [UIFontDescriptor fontDescriptorWithFontAttributes:attributes]; 33 | return [UIFont fontWithDescriptor:desc size:size]; 34 | } 35 | 36 | + (instancetype)fontWithPostscriptName:(NSString*)postscriptName size:(CGFloat)size 37 | { 38 | // UIFontDescriptorNameAttribute = Postscript name, should not change across iOS versions. 39 | NSDictionary* attributes = @{ UIFontDescriptorNameAttribute: postscriptName }; 40 | 41 | UIFontDescriptor* desc = [UIFontDescriptor fontDescriptorWithFontAttributes:attributes]; 42 | return [UIFont fontWithDescriptor:desc size:size]; 43 | } 44 | 45 | - (instancetype)fontWithSymbolicTraits:(UIFontDescriptorSymbolicTraits)symTraits 46 | { 47 | NSDictionary* attributes = @{UIFontDescriptorFamilyAttribute: self.familyName, 48 | UIFontDescriptorTraitsAttribute: @{UIFontSymbolicTrait:@(symTraits)}}; 49 | UIFontDescriptor* desc = [UIFontDescriptor fontDescriptorWithFontAttributes:attributes]; 50 | return [UIFont fontWithDescriptor:desc size:self.pointSize]; 51 | } 52 | 53 | - (UIFontDescriptorSymbolicTraits)symbolicTraits 54 | { 55 | return self.fontDescriptor.symbolicTraits; 56 | } 57 | 58 | @end 59 | -------------------------------------------------------------------------------- /Source/UILabel+OHAdditions.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * 3 | * Copyright (c) 2010 Olivier Halligon 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 | ******************************************************************************/ 24 | 25 | #import 26 | 27 | /** 28 | * Convenience methods to get text layout information from the UILabel and 29 | * especially determine which character is at a given point. 30 | */ 31 | @interface UILabel (OHAdditions) 32 | 33 | /** 34 | * Returns a new `NSTextContainer` configured with the current label's settings 35 | * (namely `maximumNumberOfLines` and `lineBreakMode`) 36 | */ 37 | @property(nonatomic, readonly) NSTextContainer* currentTextContainer; 38 | 39 | /** 40 | * The index of the character at a given point. 41 | * 42 | * Typically used to know which character was tapped on the UILabel. 43 | * 44 | * @param point The point's coordinates, expressed in the label's coordinate 45 | * system 46 | * 47 | * @return The index of the character present at that position, or 48 | * `NSNotFound` if no character is present at that point. 49 | * 50 | * @note This method is does not handle UILabel's text shrinking feature 51 | * (like when you set `adjustFontSizeToFitWidth` to `YES`). 52 | * 53 | * @note The actual `NSTextContainer` and `NSLayoutManager` used by `UILabel` 54 | * internally to layout its text are not publicly exposed. This forces 55 | * us to create and use our own `NSLayoutManager` for the computation, 56 | * which may not be configured *exactly* the same as the private one, 57 | * so slight deviations might happen (especially when text schrinking 58 | * is on or similar stuff). 59 | * 60 | * If you really need precise detection, consider using `UITextView` 61 | * (with `editable=NO`) instead, which already support all that stuff. 62 | */ 63 | - (NSUInteger)characterIndexAtPoint:(CGPoint)point; 64 | 65 | @end 66 | -------------------------------------------------------------------------------- /Source/UILabel+OHAdditions.m: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * 3 | * Copyright (c) 2010 Olivier Halligon 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 | ******************************************************************************/ 24 | 25 | #import "UILabel+OHAdditions.h" 26 | 27 | @implementation UILabel (OHAdditions) 28 | 29 | - (NSTextContainer*)currentTextContainer 30 | { 31 | NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:self.bounds.size]; 32 | textContainer.lineFragmentPadding = 0; 33 | textContainer.maximumNumberOfLines = (NSUInteger)self.numberOfLines; 34 | textContainer.lineBreakMode = self.lineBreakMode; 35 | return textContainer; 36 | } 37 | 38 | - (NSUInteger)characterIndexAtPoint:(CGPoint)point 39 | { 40 | NSTextContainer* textContainer = self.currentTextContainer; 41 | NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:self.attributedText]; 42 | 43 | NSLayoutManager *layoutManager = [NSLayoutManager new]; 44 | [textStorage addLayoutManager:layoutManager]; 45 | [layoutManager addTextContainer:textContainer]; 46 | 47 | // UILabel centers its text vertically, so adjust the point coordinates accordingly 48 | NSRange glyphRange = [layoutManager glyphRangeForTextContainer:textContainer]; 49 | CGRect wholeTextRect = [layoutManager boundingRectForGlyphRange:glyphRange 50 | inTextContainer:textContainer]; 51 | point.y -= (CGRectGetHeight(self.bounds)-CGRectGetHeight(wholeTextRect))/2; 52 | 53 | // Bail early if point outside the whole text bounding rect 54 | if (!CGRectContainsPoint(wholeTextRect, point)) return NSNotFound; 55 | 56 | // ask the layoutManager which glyph is under this tapped point 57 | NSUInteger glyphIdx = [layoutManager glyphIndexForPoint:point 58 | inTextContainer:textContainer 59 | fractionOfDistanceThroughGlyph:NULL]; 60 | 61 | // as explained in Apple's documentation the previous method returns the nearest glyph 62 | // if no glyph was present at that point. So if we want to ensure the point actually 63 | // lies on that glyph, we should check that explicitly 64 | CGRect glyphRect = [layoutManager boundingRectForGlyphRange:NSMakeRange(glyphIdx, 1) 65 | inTextContainer:textContainer]; 66 | if (CGRectContainsPoint(glyphRect, point)) 67 | { 68 | return [layoutManager characterIndexForGlyphAtIndex:glyphIdx]; 69 | } else { 70 | return NSNotFound; 71 | } 72 | } 73 | 74 | @end 75 | --------------------------------------------------------------------------------