├── .gitignore ├── .swift-version ├── .swiftlint.yml ├── .travis.yml ├── CNAME ├── CuteAttribute.podspec ├── CuteAttribute.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ └── CuteAttribute.xcscheme ├── CuteAttribute ├── Box.swift ├── CuteAttribute+Attributes.swift ├── CuteAttribute+Color.swift ├── CuteAttribute+Convert.swift ├── CuteAttribute+Match.swift ├── CuteAttribute+NSRange.swift ├── CuteAttribute+Tap.swift ├── CuteAttribute+Underline.swift ├── CuteAttribute+append.swift ├── CuteAttribute.h ├── CuteAttribute.swift ├── CuteAttributeKeys.swift ├── CuteHighlight.swift ├── DataDetectorHelper.swift ├── Info.plist ├── RegexHelper.swift ├── String+Cute.swift ├── TapableLabel.swift ├── UIColor+Cute.swift ├── UILabel+Cute.swift ├── UITextField+Cute.swift ├── UITextView+Cute.swift └── UIView+Cute.swift ├── CuteAttributeTests ├── CuteAttributeTests.swift └── Info.plist ├── Example ├── AppDelegate.swift ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Info.plist └── ViewController.swift ├── Images ├── baselineOffset.png ├── color.png ├── font.png ├── kern.png ├── ligature.png ├── link.png ├── logo.png ├── screenshot.png ├── shadow.png ├── strikethrough.png ├── stroke.png └── underline.png ├── LICENSE ├── Package.swift └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Mac OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | 6 | ## Build generated 7 | build/ 8 | DerivedData 9 | 10 | ## Various settings 11 | *.pbxuser 12 | !default.pbxuser 13 | *.mode1v3 14 | !default.mode1v3 15 | *.mode2v3 16 | !default.mode2v3 17 | *.perspectivev3 18 | !default.perspectivev3 19 | xcuserdata 20 | 21 | ## Other 22 | *.xccheckout 23 | *.moved-aside 24 | *.xcuserstate 25 | # *.xcscmblueprint 26 | 27 | ## Obj-C/Swift specific 28 | *.hmap 29 | *.ipa 30 | 31 | ## Playgrounds 32 | timeline.xctimeline 33 | playground.xcworkspace 34 | 35 | # Swift Package Manager 36 | .build/ 37 | 38 | # Carthage 39 | Carthage/Build 40 | /.idea 41 | -------------------------------------------------------------------------------- /.swift-version: -------------------------------------------------------------------------------- 1 | 4.2 -------------------------------------------------------------------------------- /.swiftlint.yml: -------------------------------------------------------------------------------- 1 | disabled_rules: 2 | - line_length 3 | - trailing_whitespace -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # references: 2 | # * http://www.objc.io/issue-6/travis-ci.html 3 | # * https://github.com/supermarin/xcpretty#usage 4 | 5 | osx_image: xcode10 6 | language: objective-c 7 | # cache: cocoapods 8 | # podfile: Example/Podfile 9 | # before_install: 10 | # - gem install cocoapods # Since Travis is not always on latest version 11 | # - pod install --project-directory=Example 12 | script: 13 | - xcodebuild -project CuteAttribute.xcodeproj -target Example -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO 14 | - pod lib lint 15 | -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | cuteattribute.vsccw.com 2 | -------------------------------------------------------------------------------- /CuteAttribute.podspec: -------------------------------------------------------------------------------- 1 | 2 | Pod::Spec.new do |s| 3 | s.name = "CuteAttribute" 4 | s.version = "1.2.5" 5 | s.summary = "A elegant way to use attributed string in swift." 6 | s.description = <<-DESC 7 | * A elegant way to deal with attributed string in swift. 8 | * It's convenient to create attributed string by `.cute`, 9 | * whatever from `String`, `NSString`, `NSAttributedString` or `NSMutableAttributedString`. 10 | * It's so amazing that almost all method support `chaining`. 11 | DESC 12 | 13 | s.homepage = "https://github.com/qiuncheng/CuteAttribute" 14 | 15 | s.license = "MIT" 16 | 17 | s.author = {"qiuncheng" => "qiuncheng@gmail.com"} 18 | s.platform = :ios, "8.0" 19 | s.source = {:git => "https://github.com/qiuncheng/CuteAttribute.git", :tag => "#{s.version}"} 20 | 21 | s.source_files = "CuteAttribute/*.{swift,h}" 22 | 23 | s.framework = "UIKit" 24 | s.requires_arc = true 25 | end 26 | -------------------------------------------------------------------------------- /CuteAttribute.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 3F30470D1F40945900F8BF5D /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F30470C1F40945900F8BF5D /* AppDelegate.swift */; }; 11 | 3F30470F1F40945900F8BF5D /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F30470E1F40945900F8BF5D /* ViewController.swift */; }; 12 | 3F3047121F40945900F8BF5D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3F3047101F40945900F8BF5D /* Main.storyboard */; }; 13 | 3F3047141F40945900F8BF5D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3F3047131F40945900F8BF5D /* Assets.xcassets */; }; 14 | 3F3047171F40945900F8BF5D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3F3047151F40945900F8BF5D /* LaunchScreen.storyboard */; }; 15 | 3F615F451F52F081006CFBCB /* CuteAttribute.h in Headers */ = {isa = PBXBuildFile; fileRef = 3F615F431F52F025006CFBCB /* CuteAttribute.h */; settings = {ATTRIBUTES = (Public, ); }; }; 16 | 3F615F561F52F8FD006CFBCB /* CuteAttribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F615F461F52F8FD006CFBCB /* CuteAttribute.swift */; }; 17 | 3F615F571F52F8FD006CFBCB /* CuteAttribute+append.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F615F471F52F8FD006CFBCB /* CuteAttribute+append.swift */; }; 18 | 3F615F581F52F8FD006CFBCB /* CuteAttribute+Attributes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F615F481F52F8FD006CFBCB /* CuteAttribute+Attributes.swift */; }; 19 | 3F615F591F52F8FD006CFBCB /* CuteAttribute+Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F615F491F52F8FD006CFBCB /* CuteAttribute+Color.swift */; }; 20 | 3F615F5A1F52F8FD006CFBCB /* CuteAttribute+Convert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F615F4A1F52F8FD006CFBCB /* CuteAttribute+Convert.swift */; }; 21 | 3F615F5B1F52F8FD006CFBCB /* CuteAttribute+Match.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F615F4B1F52F8FD006CFBCB /* CuteAttribute+Match.swift */; }; 22 | 3F615F5C1F52F8FD006CFBCB /* CuteAttribute+NSRange.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F615F4C1F52F8FD006CFBCB /* CuteAttribute+NSRange.swift */; }; 23 | 3F615F5D1F52F8FD006CFBCB /* CuteAttribute+Underline.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F615F4D1F52F8FD006CFBCB /* CuteAttribute+Underline.swift */; }; 24 | 3F615F5E1F52F8FD006CFBCB /* CuteAttributeKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F615F4E1F52F8FD006CFBCB /* CuteAttributeKeys.swift */; }; 25 | 3F615F5F1F52F8FD006CFBCB /* DataDetectorHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F615F4F1F52F8FD006CFBCB /* DataDetectorHelper.swift */; }; 26 | 3F615F601F52F8FD006CFBCB /* RegexHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F615F501F52F8FD006CFBCB /* RegexHelper.swift */; }; 27 | 3F615F611F52F8FD006CFBCB /* String+Cute.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F615F511F52F8FD006CFBCB /* String+Cute.swift */; }; 28 | 3F615F621F52F8FD006CFBCB /* UIColor+Cute.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F615F521F52F8FD006CFBCB /* UIColor+Cute.swift */; }; 29 | 3F615F631F52F8FD006CFBCB /* UILabel+Cute.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F615F531F52F8FD006CFBCB /* UILabel+Cute.swift */; }; 30 | 3F615F641F52F8FD006CFBCB /* UITextField+Cute.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F615F541F52F8FD006CFBCB /* UITextField+Cute.swift */; }; 31 | 3F615F651F52F8FD006CFBCB /* UITextView+Cute.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F615F551F52F8FD006CFBCB /* UITextView+Cute.swift */; }; 32 | 3F77D3531F8A775900B3ACDA /* CuteAttribute+Tap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F77D3521F8A775900B3ACDA /* CuteAttribute+Tap.swift */; }; 33 | 3F7A0BDB2009C5EB00952875 /* Box.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F7A0BDA2009C5EB00952875 /* Box.swift */; }; 34 | 3F7D3CBA1F406E6900409657 /* CuteAttribute.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3F7D3CB01F406E6900409657 /* CuteAttribute.framework */; }; 35 | 3F7D3CBF1F406E6900409657 /* CuteAttributeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F7D3CBE1F406E6900409657 /* CuteAttributeTests.swift */; }; 36 | 3F9668872009DA9F0021BE22 /* CuteHighlight.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F9668862009DA9F0021BE22 /* CuteHighlight.swift */; }; 37 | 3F9B002E1F41E72C0021CBDD /* CuteAttribute.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3F7D3CB01F406E6900409657 /* CuteAttribute.framework */; }; 38 | 3F9B002F1F41E72C0021CBDD /* CuteAttribute.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3F7D3CB01F406E6900409657 /* CuteAttribute.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 39 | 3FF121E91FB887B700D8DD09 /* TapableLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FF121E81FB887B700D8DD09 /* TapableLabel.swift */; }; 40 | 3FF121EB1FB8936900D8DD09 /* UIView+Cute.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FF121EA1FB8936900D8DD09 /* UIView+Cute.swift */; }; 41 | /* End PBXBuildFile section */ 42 | 43 | /* Begin PBXContainerItemProxy section */ 44 | 3F7D3CBB1F406E6900409657 /* PBXContainerItemProxy */ = { 45 | isa = PBXContainerItemProxy; 46 | containerPortal = 3F7D3CA71F406E6900409657 /* Project object */; 47 | proxyType = 1; 48 | remoteGlobalIDString = 3F7D3CAF1F406E6900409657; 49 | remoteInfo = CuteAttribute; 50 | }; 51 | 3F9B00301F41E72C0021CBDD /* PBXContainerItemProxy */ = { 52 | isa = PBXContainerItemProxy; 53 | containerPortal = 3F7D3CA71F406E6900409657 /* Project object */; 54 | proxyType = 1; 55 | remoteGlobalIDString = 3F7D3CAF1F406E6900409657; 56 | remoteInfo = CuteAttribute; 57 | }; 58 | /* End PBXContainerItemProxy section */ 59 | 60 | /* Begin PBXCopyFilesBuildPhase section */ 61 | 3F9B00321F41E72C0021CBDD /* Embed Frameworks */ = { 62 | isa = PBXCopyFilesBuildPhase; 63 | buildActionMask = 2147483647; 64 | dstPath = ""; 65 | dstSubfolderSpec = 10; 66 | files = ( 67 | 3F9B002F1F41E72C0021CBDD /* CuteAttribute.framework in Embed Frameworks */, 68 | ); 69 | name = "Embed Frameworks"; 70 | runOnlyForDeploymentPostprocessing = 0; 71 | }; 72 | /* End PBXCopyFilesBuildPhase section */ 73 | 74 | /* Begin PBXFileReference section */ 75 | 3F30470A1F40945900F8BF5D /* Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 76 | 3F30470C1F40945900F8BF5D /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 77 | 3F30470E1F40945900F8BF5D /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 78 | 3F3047111F40945900F8BF5D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 79 | 3F3047131F40945900F8BF5D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 80 | 3F3047161F40945900F8BF5D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 81 | 3F3047181F40945900F8BF5D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 82 | 3F615F3F1F52EF05006CFBCB /* CuteAttribute.podspec */ = {isa = PBXFileReference; lastKnownFileType = text; path = CuteAttribute.podspec; sourceTree = ""; }; 83 | 3F615F401F52EF05006CFBCB /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 84 | 3F615F411F52EF05006CFBCB /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; 85 | 3F615F421F52F000006CFBCB /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 86 | 3F615F431F52F025006CFBCB /* CuteAttribute.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CuteAttribute.h; sourceTree = ""; }; 87 | 3F615F461F52F8FD006CFBCB /* CuteAttribute.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CuteAttribute.swift; sourceTree = ""; }; 88 | 3F615F471F52F8FD006CFBCB /* CuteAttribute+append.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CuteAttribute+append.swift"; sourceTree = ""; }; 89 | 3F615F481F52F8FD006CFBCB /* CuteAttribute+Attributes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CuteAttribute+Attributes.swift"; sourceTree = ""; }; 90 | 3F615F491F52F8FD006CFBCB /* CuteAttribute+Color.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CuteAttribute+Color.swift"; sourceTree = ""; }; 91 | 3F615F4A1F52F8FD006CFBCB /* CuteAttribute+Convert.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CuteAttribute+Convert.swift"; sourceTree = ""; }; 92 | 3F615F4B1F52F8FD006CFBCB /* CuteAttribute+Match.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CuteAttribute+Match.swift"; sourceTree = ""; }; 93 | 3F615F4C1F52F8FD006CFBCB /* CuteAttribute+NSRange.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CuteAttribute+NSRange.swift"; sourceTree = ""; }; 94 | 3F615F4D1F52F8FD006CFBCB /* CuteAttribute+Underline.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CuteAttribute+Underline.swift"; sourceTree = ""; }; 95 | 3F615F4E1F52F8FD006CFBCB /* CuteAttributeKeys.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CuteAttributeKeys.swift; sourceTree = ""; }; 96 | 3F615F4F1F52F8FD006CFBCB /* DataDetectorHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataDetectorHelper.swift; sourceTree = ""; }; 97 | 3F615F501F52F8FD006CFBCB /* RegexHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RegexHelper.swift; sourceTree = ""; }; 98 | 3F615F511F52F8FD006CFBCB /* String+Cute.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+Cute.swift"; sourceTree = ""; }; 99 | 3F615F521F52F8FD006CFBCB /* UIColor+Cute.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIColor+Cute.swift"; sourceTree = ""; }; 100 | 3F615F531F52F8FD006CFBCB /* UILabel+Cute.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UILabel+Cute.swift"; sourceTree = ""; }; 101 | 3F615F541F52F8FD006CFBCB /* UITextField+Cute.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UITextField+Cute.swift"; sourceTree = ""; }; 102 | 3F615F551F52F8FD006CFBCB /* UITextView+Cute.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UITextView+Cute.swift"; sourceTree = ""; }; 103 | 3F77D3521F8A775900B3ACDA /* CuteAttribute+Tap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CuteAttribute+Tap.swift"; sourceTree = ""; }; 104 | 3F7A0BDA2009C5EB00952875 /* Box.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Box.swift; sourceTree = ""; }; 105 | 3F7D3CB01F406E6900409657 /* CuteAttribute.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CuteAttribute.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 106 | 3F7D3CB91F406E6900409657 /* CuteAttributeTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CuteAttributeTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 107 | 3F7D3CBE1F406E6900409657 /* CuteAttributeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CuteAttributeTests.swift; sourceTree = ""; }; 108 | 3F7D3CC01F406E6900409657 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 109 | 3F9668862009DA9F0021BE22 /* CuteHighlight.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CuteHighlight.swift; sourceTree = ""; }; 110 | 3FF121E81FB887B700D8DD09 /* TapableLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TapableLabel.swift; sourceTree = ""; }; 111 | 3FF121EA1FB8936900D8DD09 /* UIView+Cute.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Cute.swift"; sourceTree = ""; }; 112 | /* End PBXFileReference section */ 113 | 114 | /* Begin PBXFrameworksBuildPhase section */ 115 | 3F3047071F40945900F8BF5D /* Frameworks */ = { 116 | isa = PBXFrameworksBuildPhase; 117 | buildActionMask = 2147483647; 118 | files = ( 119 | 3F9B002E1F41E72C0021CBDD /* CuteAttribute.framework in Frameworks */, 120 | ); 121 | runOnlyForDeploymentPostprocessing = 0; 122 | }; 123 | 3F7D3CAC1F406E6900409657 /* Frameworks */ = { 124 | isa = PBXFrameworksBuildPhase; 125 | buildActionMask = 2147483647; 126 | files = ( 127 | ); 128 | runOnlyForDeploymentPostprocessing = 0; 129 | }; 130 | 3F7D3CB61F406E6900409657 /* Frameworks */ = { 131 | isa = PBXFrameworksBuildPhase; 132 | buildActionMask = 2147483647; 133 | files = ( 134 | 3F7D3CBA1F406E6900409657 /* CuteAttribute.framework in Frameworks */, 135 | ); 136 | runOnlyForDeploymentPostprocessing = 0; 137 | }; 138 | /* End PBXFrameworksBuildPhase section */ 139 | 140 | /* Begin PBXGroup section */ 141 | 3F30470B1F40945900F8BF5D /* Example */ = { 142 | isa = PBXGroup; 143 | children = ( 144 | 3F30470C1F40945900F8BF5D /* AppDelegate.swift */, 145 | 3F30470E1F40945900F8BF5D /* ViewController.swift */, 146 | 3F3047101F40945900F8BF5D /* Main.storyboard */, 147 | 3F3047131F40945900F8BF5D /* Assets.xcassets */, 148 | 3F3047151F40945900F8BF5D /* LaunchScreen.storyboard */, 149 | 3F3047181F40945900F8BF5D /* Info.plist */, 150 | ); 151 | path = Example; 152 | sourceTree = ""; 153 | }; 154 | 3F615F661F52F90B006CFBCB /* Core */ = { 155 | isa = PBXGroup; 156 | children = ( 157 | 3F615F461F52F8FD006CFBCB /* CuteAttribute.swift */, 158 | 3F615F471F52F8FD006CFBCB /* CuteAttribute+append.swift */, 159 | 3F615F481F52F8FD006CFBCB /* CuteAttribute+Attributes.swift */, 160 | 3F615F491F52F8FD006CFBCB /* CuteAttribute+Color.swift */, 161 | 3F615F4A1F52F8FD006CFBCB /* CuteAttribute+Convert.swift */, 162 | 3F615F4B1F52F8FD006CFBCB /* CuteAttribute+Match.swift */, 163 | 3F615F4C1F52F8FD006CFBCB /* CuteAttribute+NSRange.swift */, 164 | 3F615F4D1F52F8FD006CFBCB /* CuteAttribute+Underline.swift */, 165 | 3F77D3521F8A775900B3ACDA /* CuteAttribute+Tap.swift */, 166 | 3F9668862009DA9F0021BE22 /* CuteHighlight.swift */, 167 | ); 168 | name = Core; 169 | sourceTree = ""; 170 | }; 171 | 3F615F671F52F910006CFBCB /* UIKit */ = { 172 | isa = PBXGroup; 173 | children = ( 174 | 3F615F531F52F8FD006CFBCB /* UILabel+Cute.swift */, 175 | 3FF121E81FB887B700D8DD09 /* TapableLabel.swift */, 176 | 3F615F541F52F8FD006CFBCB /* UITextField+Cute.swift */, 177 | 3F615F551F52F8FD006CFBCB /* UITextView+Cute.swift */, 178 | 3FF121EA1FB8936900D8DD09 /* UIView+Cute.swift */, 179 | 3F615F521F52F8FD006CFBCB /* UIColor+Cute.swift */, 180 | ); 181 | name = UIKit; 182 | sourceTree = ""; 183 | }; 184 | 3F7A0BD92009C5DC00952875 /* Helper */ = { 185 | isa = PBXGroup; 186 | children = ( 187 | 3F615F4F1F52F8FD006CFBCB /* DataDetectorHelper.swift */, 188 | 3F615F501F52F8FD006CFBCB /* RegexHelper.swift */, 189 | 3F615F4E1F52F8FD006CFBCB /* CuteAttributeKeys.swift */, 190 | 3F615F511F52F8FD006CFBCB /* String+Cute.swift */, 191 | 3F7A0BDA2009C5EB00952875 /* Box.swift */, 192 | ); 193 | name = Helper; 194 | sourceTree = ""; 195 | }; 196 | 3F7D3CA61F406E6900409657 = { 197 | isa = PBXGroup; 198 | children = ( 199 | 3F615F3F1F52EF05006CFBCB /* CuteAttribute.podspec */, 200 | 3F615F401F52EF05006CFBCB /* README.md */, 201 | 3F615F411F52EF05006CFBCB /* LICENSE */, 202 | 3F7D3CB21F406E6900409657 /* CuteAttribute */, 203 | 3F7D3CBD1F406E6900409657 /* CuteAttributeTests */, 204 | 3F30470B1F40945900F8BF5D /* Example */, 205 | 3F7D3CB11F406E6900409657 /* Products */, 206 | ); 207 | indentWidth = 4; 208 | sourceTree = ""; 209 | tabWidth = 4; 210 | }; 211 | 3F7D3CB11F406E6900409657 /* Products */ = { 212 | isa = PBXGroup; 213 | children = ( 214 | 3F7D3CB01F406E6900409657 /* CuteAttribute.framework */, 215 | 3F7D3CB91F406E6900409657 /* CuteAttributeTests.xctest */, 216 | 3F30470A1F40945900F8BF5D /* Example.app */, 217 | ); 218 | name = Products; 219 | sourceTree = ""; 220 | }; 221 | 3F7D3CB21F406E6900409657 /* CuteAttribute */ = { 222 | isa = PBXGroup; 223 | children = ( 224 | 3F615F431F52F025006CFBCB /* CuteAttribute.h */, 225 | 3F615F421F52F000006CFBCB /* Info.plist */, 226 | 3F7A0BD92009C5DC00952875 /* Helper */, 227 | 3F615F661F52F90B006CFBCB /* Core */, 228 | 3F615F671F52F910006CFBCB /* UIKit */, 229 | ); 230 | path = CuteAttribute; 231 | sourceTree = ""; 232 | }; 233 | 3F7D3CBD1F406E6900409657 /* CuteAttributeTests */ = { 234 | isa = PBXGroup; 235 | children = ( 236 | 3F7D3CBE1F406E6900409657 /* CuteAttributeTests.swift */, 237 | 3F7D3CC01F406E6900409657 /* Info.plist */, 238 | ); 239 | path = CuteAttributeTests; 240 | sourceTree = ""; 241 | }; 242 | /* End PBXGroup section */ 243 | 244 | /* Begin PBXHeadersBuildPhase section */ 245 | 3F7D3CAD1F406E6900409657 /* Headers */ = { 246 | isa = PBXHeadersBuildPhase; 247 | buildActionMask = 2147483647; 248 | files = ( 249 | 3F615F451F52F081006CFBCB /* CuteAttribute.h in Headers */, 250 | ); 251 | runOnlyForDeploymentPostprocessing = 0; 252 | }; 253 | /* End PBXHeadersBuildPhase section */ 254 | 255 | /* Begin PBXNativeTarget section */ 256 | 3F3047091F40945900F8BF5D /* Example */ = { 257 | isa = PBXNativeTarget; 258 | buildConfigurationList = 3F3047191F40945900F8BF5D /* Build configuration list for PBXNativeTarget "Example" */; 259 | buildPhases = ( 260 | 3F3047061F40945900F8BF5D /* Sources */, 261 | 3F3047071F40945900F8BF5D /* Frameworks */, 262 | 3F3047081F40945900F8BF5D /* Resources */, 263 | 3F9B00321F41E72C0021CBDD /* Embed Frameworks */, 264 | ); 265 | buildRules = ( 266 | ); 267 | dependencies = ( 268 | 3F9B00311F41E72C0021CBDD /* PBXTargetDependency */, 269 | ); 270 | name = Example; 271 | productName = Example; 272 | productReference = 3F30470A1F40945900F8BF5D /* Example.app */; 273 | productType = "com.apple.product-type.application"; 274 | }; 275 | 3F7D3CAF1F406E6900409657 /* CuteAttribute */ = { 276 | isa = PBXNativeTarget; 277 | buildConfigurationList = 3F7D3CC41F406E6900409657 /* Build configuration list for PBXNativeTarget "CuteAttribute" */; 278 | buildPhases = ( 279 | 3F7D3CAB1F406E6900409657 /* Sources */, 280 | 3F7D3CAC1F406E6900409657 /* Frameworks */, 281 | 3F7D3CAD1F406E6900409657 /* Headers */, 282 | 86326435208B59540018C406 /* ShellScript */, 283 | ); 284 | buildRules = ( 285 | ); 286 | dependencies = ( 287 | ); 288 | name = CuteAttribute; 289 | productName = CuteAttribute; 290 | productReference = 3F7D3CB01F406E6900409657 /* CuteAttribute.framework */; 291 | productType = "com.apple.product-type.framework"; 292 | }; 293 | 3F7D3CB81F406E6900409657 /* CuteAttributeTests */ = { 294 | isa = PBXNativeTarget; 295 | buildConfigurationList = 3F7D3CC71F406E6900409657 /* Build configuration list for PBXNativeTarget "CuteAttributeTests" */; 296 | buildPhases = ( 297 | 3F7D3CB51F406E6900409657 /* Sources */, 298 | 3F7D3CB61F406E6900409657 /* Frameworks */, 299 | 3F7D3CB71F406E6900409657 /* Resources */, 300 | ); 301 | buildRules = ( 302 | ); 303 | dependencies = ( 304 | 3F7D3CBC1F406E6900409657 /* PBXTargetDependency */, 305 | ); 306 | name = CuteAttributeTests; 307 | productName = CuteAttributeTests; 308 | productReference = 3F7D3CB91F406E6900409657 /* CuteAttributeTests.xctest */; 309 | productType = "com.apple.product-type.bundle.unit-test"; 310 | }; 311 | /* End PBXNativeTarget section */ 312 | 313 | /* Begin PBXProject section */ 314 | 3F7D3CA71F406E6900409657 /* Project object */ = { 315 | isa = PBXProject; 316 | attributes = { 317 | LastSwiftUpdateCheck = 0830; 318 | LastUpgradeCheck = 1030; 319 | ORGANIZATIONNAME = "https://vsccw.com"; 320 | TargetAttributes = { 321 | 3F3047091F40945900F8BF5D = { 322 | CreatedOnToolsVersion = 8.3.3; 323 | DevelopmentTeam = 7Z9QGDZHJM; 324 | LastSwiftMigration = 1030; 325 | ProvisioningStyle = Automatic; 326 | }; 327 | 3F7D3CAF1F406E6900409657 = { 328 | CreatedOnToolsVersion = 8.3.3; 329 | DevelopmentTeam = 7Z9QGDZHJM; 330 | LastSwiftMigration = 1030; 331 | ProvisioningStyle = Automatic; 332 | }; 333 | 3F7D3CB81F406E6900409657 = { 334 | CreatedOnToolsVersion = 8.3.3; 335 | LastSwiftMigration = 1010; 336 | ProvisioningStyle = Automatic; 337 | }; 338 | }; 339 | }; 340 | buildConfigurationList = 3F7D3CAA1F406E6900409657 /* Build configuration list for PBXProject "CuteAttribute" */; 341 | compatibilityVersion = "Xcode 3.2"; 342 | developmentRegion = en; 343 | hasScannedForEncodings = 0; 344 | knownRegions = ( 345 | en, 346 | Base, 347 | ); 348 | mainGroup = 3F7D3CA61F406E6900409657; 349 | productRefGroup = 3F7D3CB11F406E6900409657 /* Products */; 350 | projectDirPath = ""; 351 | projectRoot = ""; 352 | targets = ( 353 | 3F7D3CAF1F406E6900409657 /* CuteAttribute */, 354 | 3F7D3CB81F406E6900409657 /* CuteAttributeTests */, 355 | 3F3047091F40945900F8BF5D /* Example */, 356 | ); 357 | }; 358 | /* End PBXProject section */ 359 | 360 | /* Begin PBXResourcesBuildPhase section */ 361 | 3F3047081F40945900F8BF5D /* Resources */ = { 362 | isa = PBXResourcesBuildPhase; 363 | buildActionMask = 2147483647; 364 | files = ( 365 | 3F3047171F40945900F8BF5D /* LaunchScreen.storyboard in Resources */, 366 | 3F3047141F40945900F8BF5D /* Assets.xcassets in Resources */, 367 | 3F3047121F40945900F8BF5D /* Main.storyboard in Resources */, 368 | ); 369 | runOnlyForDeploymentPostprocessing = 0; 370 | }; 371 | 3F7D3CB71F406E6900409657 /* Resources */ = { 372 | isa = PBXResourcesBuildPhase; 373 | buildActionMask = 2147483647; 374 | files = ( 375 | ); 376 | runOnlyForDeploymentPostprocessing = 0; 377 | }; 378 | /* End PBXResourcesBuildPhase section */ 379 | 380 | /* Begin PBXShellScriptBuildPhase section */ 381 | 86326435208B59540018C406 /* ShellScript */ = { 382 | isa = PBXShellScriptBuildPhase; 383 | buildActionMask = 2147483647; 384 | files = ( 385 | ); 386 | inputPaths = ( 387 | ); 388 | outputPaths = ( 389 | ); 390 | runOnlyForDeploymentPostprocessing = 0; 391 | shellPath = /bin/sh; 392 | shellScript = "if which swiftlint >/dev/null; then\nswiftlint\nelse\necho \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n"; 393 | }; 394 | /* End PBXShellScriptBuildPhase section */ 395 | 396 | /* Begin PBXSourcesBuildPhase section */ 397 | 3F3047061F40945900F8BF5D /* Sources */ = { 398 | isa = PBXSourcesBuildPhase; 399 | buildActionMask = 2147483647; 400 | files = ( 401 | 3F30470F1F40945900F8BF5D /* ViewController.swift in Sources */, 402 | 3F30470D1F40945900F8BF5D /* AppDelegate.swift in Sources */, 403 | ); 404 | runOnlyForDeploymentPostprocessing = 0; 405 | }; 406 | 3F7D3CAB1F406E6900409657 /* Sources */ = { 407 | isa = PBXSourcesBuildPhase; 408 | buildActionMask = 2147483647; 409 | files = ( 410 | 3F615F5E1F52F8FD006CFBCB /* CuteAttributeKeys.swift in Sources */, 411 | 3F77D3531F8A775900B3ACDA /* CuteAttribute+Tap.swift in Sources */, 412 | 3F615F5F1F52F8FD006CFBCB /* DataDetectorHelper.swift in Sources */, 413 | 3F615F561F52F8FD006CFBCB /* CuteAttribute.swift in Sources */, 414 | 3F615F631F52F8FD006CFBCB /* UILabel+Cute.swift in Sources */, 415 | 3F615F571F52F8FD006CFBCB /* CuteAttribute+append.swift in Sources */, 416 | 3FF121E91FB887B700D8DD09 /* TapableLabel.swift in Sources */, 417 | 3F615F581F52F8FD006CFBCB /* CuteAttribute+Attributes.swift in Sources */, 418 | 3F615F641F52F8FD006CFBCB /* UITextField+Cute.swift in Sources */, 419 | 3F7A0BDB2009C5EB00952875 /* Box.swift in Sources */, 420 | 3F9668872009DA9F0021BE22 /* CuteHighlight.swift in Sources */, 421 | 3F615F5A1F52F8FD006CFBCB /* CuteAttribute+Convert.swift in Sources */, 422 | 3F615F5C1F52F8FD006CFBCB /* CuteAttribute+NSRange.swift in Sources */, 423 | 3F615F591F52F8FD006CFBCB /* CuteAttribute+Color.swift in Sources */, 424 | 3F615F651F52F8FD006CFBCB /* UITextView+Cute.swift in Sources */, 425 | 3F615F5D1F52F8FD006CFBCB /* CuteAttribute+Underline.swift in Sources */, 426 | 3F615F5B1F52F8FD006CFBCB /* CuteAttribute+Match.swift in Sources */, 427 | 3F615F601F52F8FD006CFBCB /* RegexHelper.swift in Sources */, 428 | 3F615F611F52F8FD006CFBCB /* String+Cute.swift in Sources */, 429 | 3F615F621F52F8FD006CFBCB /* UIColor+Cute.swift in Sources */, 430 | 3FF121EB1FB8936900D8DD09 /* UIView+Cute.swift in Sources */, 431 | ); 432 | runOnlyForDeploymentPostprocessing = 0; 433 | }; 434 | 3F7D3CB51F406E6900409657 /* Sources */ = { 435 | isa = PBXSourcesBuildPhase; 436 | buildActionMask = 2147483647; 437 | files = ( 438 | 3F7D3CBF1F406E6900409657 /* CuteAttributeTests.swift in Sources */, 439 | ); 440 | runOnlyForDeploymentPostprocessing = 0; 441 | }; 442 | /* End PBXSourcesBuildPhase section */ 443 | 444 | /* Begin PBXTargetDependency section */ 445 | 3F7D3CBC1F406E6900409657 /* PBXTargetDependency */ = { 446 | isa = PBXTargetDependency; 447 | target = 3F7D3CAF1F406E6900409657 /* CuteAttribute */; 448 | targetProxy = 3F7D3CBB1F406E6900409657 /* PBXContainerItemProxy */; 449 | }; 450 | 3F9B00311F41E72C0021CBDD /* PBXTargetDependency */ = { 451 | isa = PBXTargetDependency; 452 | target = 3F7D3CAF1F406E6900409657 /* CuteAttribute */; 453 | targetProxy = 3F9B00301F41E72C0021CBDD /* PBXContainerItemProxy */; 454 | }; 455 | /* End PBXTargetDependency section */ 456 | 457 | /* Begin PBXVariantGroup section */ 458 | 3F3047101F40945900F8BF5D /* Main.storyboard */ = { 459 | isa = PBXVariantGroup; 460 | children = ( 461 | 3F3047111F40945900F8BF5D /* Base */, 462 | ); 463 | name = Main.storyboard; 464 | sourceTree = ""; 465 | }; 466 | 3F3047151F40945900F8BF5D /* LaunchScreen.storyboard */ = { 467 | isa = PBXVariantGroup; 468 | children = ( 469 | 3F3047161F40945900F8BF5D /* Base */, 470 | ); 471 | name = LaunchScreen.storyboard; 472 | sourceTree = ""; 473 | }; 474 | /* End PBXVariantGroup section */ 475 | 476 | /* Begin XCBuildConfiguration section */ 477 | 3F30471A1F40945900F8BF5D /* Debug */ = { 478 | isa = XCBuildConfiguration; 479 | buildSettings = { 480 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 481 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 482 | DEVELOPMENT_TEAM = 7Z9QGDZHJM; 483 | INFOPLIST_FILE = Example/Info.plist; 484 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 485 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 486 | PRODUCT_BUNDLE_IDENTIFIER = com.vsccw.Example; 487 | PRODUCT_NAME = "$(TARGET_NAME)"; 488 | SWIFT_VERSION = 5.0; 489 | }; 490 | name = Debug; 491 | }; 492 | 3F30471B1F40945900F8BF5D /* Release */ = { 493 | isa = XCBuildConfiguration; 494 | buildSettings = { 495 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 496 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 497 | DEVELOPMENT_TEAM = 7Z9QGDZHJM; 498 | INFOPLIST_FILE = Example/Info.plist; 499 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 500 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 501 | PRODUCT_BUNDLE_IDENTIFIER = com.vsccw.Example; 502 | PRODUCT_NAME = "$(TARGET_NAME)"; 503 | SWIFT_VERSION = 5.0; 504 | }; 505 | name = Release; 506 | }; 507 | 3F7D3CC21F406E6900409657 /* Debug */ = { 508 | isa = XCBuildConfiguration; 509 | buildSettings = { 510 | ALWAYS_SEARCH_USER_PATHS = NO; 511 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 512 | CLANG_ANALYZER_NONNULL = YES; 513 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 514 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 515 | CLANG_CXX_LIBRARY = "libc++"; 516 | CLANG_ENABLE_MODULES = YES; 517 | CLANG_ENABLE_OBJC_ARC = YES; 518 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 519 | CLANG_WARN_BOOL_CONVERSION = YES; 520 | CLANG_WARN_COMMA = YES; 521 | CLANG_WARN_CONSTANT_CONVERSION = YES; 522 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 523 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 524 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 525 | CLANG_WARN_EMPTY_BODY = YES; 526 | CLANG_WARN_ENUM_CONVERSION = YES; 527 | CLANG_WARN_INFINITE_RECURSION = YES; 528 | CLANG_WARN_INT_CONVERSION = YES; 529 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 530 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 531 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 532 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 533 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 534 | CLANG_WARN_STRICT_PROTOTYPES = YES; 535 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 536 | CLANG_WARN_UNREACHABLE_CODE = YES; 537 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 538 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 539 | COPY_PHASE_STRIP = NO; 540 | CURRENT_PROJECT_VERSION = 1; 541 | DEBUG_INFORMATION_FORMAT = dwarf; 542 | ENABLE_STRICT_OBJC_MSGSEND = YES; 543 | ENABLE_TESTABILITY = YES; 544 | GCC_C_LANGUAGE_STANDARD = gnu99; 545 | GCC_DYNAMIC_NO_PIC = NO; 546 | GCC_NO_COMMON_BLOCKS = YES; 547 | GCC_OPTIMIZATION_LEVEL = 0; 548 | GCC_PREPROCESSOR_DEFINITIONS = ( 549 | "DEBUG=1", 550 | "$(inherited)", 551 | ); 552 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 553 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 554 | GCC_WARN_UNDECLARED_SELECTOR = YES; 555 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 556 | GCC_WARN_UNUSED_FUNCTION = YES; 557 | GCC_WARN_UNUSED_VARIABLE = YES; 558 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 559 | MTL_ENABLE_DEBUG_INFO = YES; 560 | ONLY_ACTIVE_ARCH = YES; 561 | SDKROOT = iphoneos; 562 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 563 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 564 | TARGETED_DEVICE_FAMILY = "1,2"; 565 | VERSIONING_SYSTEM = "apple-generic"; 566 | VERSION_INFO_PREFIX = ""; 567 | }; 568 | name = Debug; 569 | }; 570 | 3F7D3CC31F406E6900409657 /* Release */ = { 571 | isa = XCBuildConfiguration; 572 | buildSettings = { 573 | ALWAYS_SEARCH_USER_PATHS = NO; 574 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 575 | CLANG_ANALYZER_NONNULL = YES; 576 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 577 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 578 | CLANG_CXX_LIBRARY = "libc++"; 579 | CLANG_ENABLE_MODULES = YES; 580 | CLANG_ENABLE_OBJC_ARC = YES; 581 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 582 | CLANG_WARN_BOOL_CONVERSION = YES; 583 | CLANG_WARN_COMMA = YES; 584 | CLANG_WARN_CONSTANT_CONVERSION = YES; 585 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 586 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 587 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 588 | CLANG_WARN_EMPTY_BODY = YES; 589 | CLANG_WARN_ENUM_CONVERSION = YES; 590 | CLANG_WARN_INFINITE_RECURSION = YES; 591 | CLANG_WARN_INT_CONVERSION = YES; 592 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 593 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 594 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 595 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 596 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 597 | CLANG_WARN_STRICT_PROTOTYPES = YES; 598 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 599 | CLANG_WARN_UNREACHABLE_CODE = YES; 600 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 601 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 602 | COPY_PHASE_STRIP = NO; 603 | CURRENT_PROJECT_VERSION = 1; 604 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 605 | ENABLE_NS_ASSERTIONS = NO; 606 | ENABLE_STRICT_OBJC_MSGSEND = YES; 607 | GCC_C_LANGUAGE_STANDARD = gnu99; 608 | GCC_NO_COMMON_BLOCKS = YES; 609 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 610 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 611 | GCC_WARN_UNDECLARED_SELECTOR = YES; 612 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 613 | GCC_WARN_UNUSED_FUNCTION = YES; 614 | GCC_WARN_UNUSED_VARIABLE = YES; 615 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 616 | MTL_ENABLE_DEBUG_INFO = NO; 617 | SDKROOT = iphoneos; 618 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 619 | TARGETED_DEVICE_FAMILY = "1,2"; 620 | VALIDATE_PRODUCT = YES; 621 | VERSIONING_SYSTEM = "apple-generic"; 622 | VERSION_INFO_PREFIX = ""; 623 | }; 624 | name = Release; 625 | }; 626 | 3F7D3CC51F406E6900409657 /* Debug */ = { 627 | isa = XCBuildConfiguration; 628 | buildSettings = { 629 | CLANG_ENABLE_MODULES = YES; 630 | CODE_SIGN_IDENTITY = ""; 631 | DEFINES_MODULE = YES; 632 | DEVELOPMENT_TEAM = 7Z9QGDZHJM; 633 | DYLIB_COMPATIBILITY_VERSION = 1; 634 | DYLIB_CURRENT_VERSION = 1; 635 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 636 | INFOPLIST_FILE = CuteAttribute/Info.plist; 637 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 638 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 639 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 640 | PRODUCT_BUNDLE_IDENTIFIER = com.vsccw.CuteAttribute; 641 | PRODUCT_NAME = "$(TARGET_NAME)"; 642 | SKIP_INSTALL = YES; 643 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 644 | SWIFT_VERSION = 5.0; 645 | }; 646 | name = Debug; 647 | }; 648 | 3F7D3CC61F406E6900409657 /* Release */ = { 649 | isa = XCBuildConfiguration; 650 | buildSettings = { 651 | CLANG_ENABLE_MODULES = YES; 652 | CODE_SIGN_IDENTITY = ""; 653 | DEFINES_MODULE = YES; 654 | DEVELOPMENT_TEAM = 7Z9QGDZHJM; 655 | DYLIB_COMPATIBILITY_VERSION = 1; 656 | DYLIB_CURRENT_VERSION = 1; 657 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 658 | INFOPLIST_FILE = CuteAttribute/Info.plist; 659 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 660 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 661 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 662 | PRODUCT_BUNDLE_IDENTIFIER = com.vsccw.CuteAttribute; 663 | PRODUCT_NAME = "$(TARGET_NAME)"; 664 | SKIP_INSTALL = YES; 665 | SWIFT_VERSION = 5.0; 666 | }; 667 | name = Release; 668 | }; 669 | 3F7D3CC81F406E6900409657 /* Debug */ = { 670 | isa = XCBuildConfiguration; 671 | buildSettings = { 672 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 673 | INFOPLIST_FILE = CuteAttributeTests/Info.plist; 674 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 675 | PRODUCT_BUNDLE_IDENTIFIER = com.vsccw.CuteAttributeTests; 676 | PRODUCT_NAME = "$(TARGET_NAME)"; 677 | SWIFT_SWIFT3_OBJC_INFERENCE = Off; 678 | SWIFT_VERSION = 4.2; 679 | }; 680 | name = Debug; 681 | }; 682 | 3F7D3CC91F406E6900409657 /* Release */ = { 683 | isa = XCBuildConfiguration; 684 | buildSettings = { 685 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 686 | INFOPLIST_FILE = CuteAttributeTests/Info.plist; 687 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 688 | PRODUCT_BUNDLE_IDENTIFIER = com.vsccw.CuteAttributeTests; 689 | PRODUCT_NAME = "$(TARGET_NAME)"; 690 | SWIFT_SWIFT3_OBJC_INFERENCE = Off; 691 | SWIFT_VERSION = 4.2; 692 | }; 693 | name = Release; 694 | }; 695 | /* End XCBuildConfiguration section */ 696 | 697 | /* Begin XCConfigurationList section */ 698 | 3F3047191F40945900F8BF5D /* Build configuration list for PBXNativeTarget "Example" */ = { 699 | isa = XCConfigurationList; 700 | buildConfigurations = ( 701 | 3F30471A1F40945900F8BF5D /* Debug */, 702 | 3F30471B1F40945900F8BF5D /* Release */, 703 | ); 704 | defaultConfigurationIsVisible = 0; 705 | defaultConfigurationName = Release; 706 | }; 707 | 3F7D3CAA1F406E6900409657 /* Build configuration list for PBXProject "CuteAttribute" */ = { 708 | isa = XCConfigurationList; 709 | buildConfigurations = ( 710 | 3F7D3CC21F406E6900409657 /* Debug */, 711 | 3F7D3CC31F406E6900409657 /* Release */, 712 | ); 713 | defaultConfigurationIsVisible = 0; 714 | defaultConfigurationName = Release; 715 | }; 716 | 3F7D3CC41F406E6900409657 /* Build configuration list for PBXNativeTarget "CuteAttribute" */ = { 717 | isa = XCConfigurationList; 718 | buildConfigurations = ( 719 | 3F7D3CC51F406E6900409657 /* Debug */, 720 | 3F7D3CC61F406E6900409657 /* Release */, 721 | ); 722 | defaultConfigurationIsVisible = 0; 723 | defaultConfigurationName = Release; 724 | }; 725 | 3F7D3CC71F406E6900409657 /* Build configuration list for PBXNativeTarget "CuteAttributeTests" */ = { 726 | isa = XCConfigurationList; 727 | buildConfigurations = ( 728 | 3F7D3CC81F406E6900409657 /* Debug */, 729 | 3F7D3CC91F406E6900409657 /* Release */, 730 | ); 731 | defaultConfigurationIsVisible = 0; 732 | defaultConfigurationName = Release; 733 | }; 734 | /* End XCConfigurationList section */ 735 | }; 736 | rootObject = 3F7D3CA71F406E6900409657 /* Project object */; 737 | } 738 | -------------------------------------------------------------------------------- /CuteAttribute.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /CuteAttribute.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /CuteAttribute.xcodeproj/xcshareddata/xcschemes/CuteAttribute.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 65 | 71 | 72 | 73 | 74 | 75 | 76 | 82 | 83 | 89 | 90 | 91 | 92 | 94 | 95 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /CuteAttribute/Box.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Box.swift 3 | // CuteAttribute 4 | // 5 | // Created by vsccw on 2018/1/13. 6 | // Copyright © 2018年 https://vsccw.com. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class Box { 12 | 13 | let value: V 14 | init(_ value: V) { 15 | self.value = value 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /CuteAttribute/CuteAttribute+Attributes.swift: -------------------------------------------------------------------------------- 1 | // 2 | // String+Cute.swift 3 | // Cute 4 | // 5 | // Created by vsccw on 2017/8/9. 6 | // Copyright © 2017年 https://vsccw.com. All rights reserved. 7 | // 8 | 9 | import UIKit.UIColor 10 | 11 | public extension CuteAttribute where Base: NSMutableAttributedString { 12 | 13 | /// Set color for `NSForegroundColorAttributeName`, 14 | /// this can be called after `range(_:)`, or not the range is default all the string. 15 | /// 16 | /// - Parameter color: UIColor type without default value. 17 | /// - Returns: self 18 | func color(_ color: UIColor) -> CuteAttribute { 19 | ranges.forEach { 20 | base.addAttribute(.foregroundColor, value: color, range: $0) 21 | } 22 | return self 23 | } 24 | 25 | /// Set color for `NSAttributedString.Key.backgroundColor`, 26 | /// this can be called after `range(_:)`, or not the range is default all the string. 27 | /// 28 | /// - Parameter color: UIColor type without default value. 29 | /// - Returns: self 30 | func backgroundColor(_ color: UIColor) -> CuteAttribute { 31 | ranges.forEach { 32 | base.addAttribute(.backgroundColor, value: color, range: $0) 33 | } 34 | return self 35 | } 36 | 37 | /// Set baseline for `NSBaselineOffsetAttributeName`, 38 | /// this can be called after `range(_:)`, or not the range is default all the string. 39 | /// 40 | /// - Parameter baseline: CGFloat type without default value. 41 | /// - Returns: self 42 | func baseline(_ baseline: CGFloat) -> CuteAttribute { 43 | ranges.forEach { 44 | base.addAttribute(.baselineOffset, value: baseline, range: $0) 45 | } 46 | return self 47 | } 48 | 49 | /// Set underline for `NSUnderlineStyleAttributeName`, 50 | /// this can be called after `range(_:)`, or not the range is default all the string. 51 | /// 52 | /// - Parameter underline: NSUnderlineStyle type without default value. 53 | /// - Returns: self 54 | func underline(_ underline: NSUnderlineStyle) -> CuteAttribute { 55 | ranges.forEach { 56 | base.addAttribute(.underlineStyle, value: underline.rawValue, range: $0) 57 | } 58 | return self 59 | } 60 | 61 | /// Set underlineColor for `NSUnderlineColorAttributeName`, 62 | /// this can be called after `range(_:)`, or not the range is default all the string. 63 | /// 64 | /// - Parameter color: UIColor type without default value. 65 | /// - Returns: self 66 | func underlineColor(_ color: UIColor) -> CuteAttribute { 67 | ranges.forEach { 68 | base.addAttribute(.underlineColor, value: color, range: $0) 69 | } 70 | return self 71 | } 72 | 73 | /// Set underlineStyle for `NSUnderlineStyleAttributeName`, 74 | /// this can be called after `range(_:)`, or not the range is default all the string. 75 | /// 76 | /// - Parameter underlineStyle: NSUnderlineStyle type without default value. 77 | /// - Parameter color: UIColor type without default value. 78 | /// - Returns: self 79 | func underline(_ underlineStyle: NSUnderlineStyle, color: UIColor) -> CuteAttribute { 80 | ranges.forEach { 81 | base.addAttributes([.underlineStyle: underlineStyle.rawValue, .underlineColor: color], range: $0) 82 | } 83 | return self 84 | } 85 | 86 | /// Set font for `NSFontAttributeName`, 87 | /// this can be called after `range(_:)`, or not the range is default all the string. 88 | /// 89 | /// - Parameter font: UIFont type without default value. 90 | /// - Returns: self 91 | func font(_ font: UIFont) -> CuteAttribute { 92 | ranges.forEach { 93 | base.addAttribute(.font, value: font, range: $0) 94 | } 95 | return self 96 | } 97 | 98 | /// Set fontName for `NSFontAttributeName`, 99 | /// this can be called after `range(_:)`, or not the range is default all the string. 100 | /// 101 | /// - Parameter name: String type without default value. 102 | /// - Returns: self 103 | func fontName(_ name: String) -> CuteAttribute { 104 | ranges.forEach { 105 | var mRange = $0 106 | let exitedFont = base.attribute(.font, at: 0, effectiveRange: &mRange) as? UIFont 107 | let size = exitedFont?.pointSize ?? 17.0 108 | let font = UIFont(name: name, size: size) ?? UIFont.systemFont(ofSize: size) 109 | base.addAttribute(.font, value: font, range: $0) 110 | } 111 | return self 112 | } 113 | 114 | /// Set strikeThrough for `NSStrikethroughStyleAttributeName`, 115 | /// this can be called after `range(_:)`, or not the range is default all the string. 116 | /// 117 | /// - Parameter strike: NSUnderlineStyle type without default value. 118 | /// - Returns: self 119 | func strikeThrough(_ strike: NSUnderlineStyle) -> CuteAttribute { 120 | ranges.forEach { 121 | base.addAttribute(.strikethroughStyle, value: strike.rawValue, range: $0) 122 | } 123 | return self 124 | } 125 | 126 | /// Set strikeThroughColor for `NSStrikethroughColorAttributeName`, 127 | /// this can be called after `range(_:)`, or not the range is default all the string. 128 | /// 129 | /// - Parameter color: UIColor type without default value. 130 | /// - Returns: self 131 | func strikeThroughColor(_ color: UIColor) -> CuteAttribute { 132 | ranges.forEach { 133 | base.addAttribute(.strikethroughColor, value: color, range: $0) 134 | } 135 | return self 136 | } 137 | 138 | /// Set link for `NSLinkAttributeName`, 139 | /// this can be called after `range(_:)`, 140 | /// or not the range is default all the string. 141 | /// 142 | /// - Parameter link: String type without default value. 143 | /// - Returns: self 144 | func link(_ link: String) -> CuteAttribute { 145 | ranges.forEach { 146 | base.addAttribute(.link, value: link, range: $0) 147 | } 148 | return self 149 | } 150 | 151 | /// Set link for `NSLinkAttributeName`, 152 | /// this can be called after `range(_:)`, 153 | /// or not the range is default all the string. 154 | /// 155 | /// - Parameter url: URL type without default value. 156 | /// - Returns: self 157 | func link(_ url: URL) -> CuteAttribute { 158 | ranges.forEach { 159 | base.addAttribute(.link, value: url, range: $0) 160 | } 161 | return self 162 | } 163 | 164 | /// Set ligature for `NSLigatureAttributeName`, 165 | /// this can be called after `range(_:)`, 166 | /// or not the range is default all the string. 167 | /// 168 | /// - Parameter ligature: Int type without default value. 169 | /// - Returns: self 170 | func ligature(_ ligature: Int) -> CuteAttribute { 171 | ranges.forEach { 172 | base.addAttribute(.ligature, value: ligature, range: $0) 173 | } 174 | return self 175 | } 176 | 177 | /// Set kern for `NSKernAttributeName`, 178 | /// this can be called after `range(_:)`, 179 | /// or not the range is default all the string. 180 | /// 181 | /// - Parameter kern: CGFloat type without default value. 182 | /// - Returns: self 183 | func kern(_ kern: CGFloat) -> CuteAttribute { 184 | ranges.forEach { 185 | base.addAttribute(.kern, value: kern, range: $0) 186 | } 187 | return self 188 | } 189 | 190 | /// Set strokeColor for `NSStrokeColorAttributeName`, 191 | /// this can be called after `range(_:)`, 192 | /// or not the range is default all the string. 193 | /// 194 | /// - Parameter color: UIColor type without default value. 195 | /// - Returns: self 196 | func strokeColor(_ color: UIColor) -> CuteAttribute { 197 | ranges.forEach { 198 | base.addAttribute(.strokeColor, value: color, range: $0) 199 | } 200 | return self 201 | } 202 | 203 | /// Set strokeWidth for `NSStrokeWidthAttributeName`, 204 | /// this can be called after `range(_:)`, 205 | /// or not the range is default all the string. 206 | /// 207 | /// - Parameter width: CGFloat type without default value. 208 | /// - Returns: self 209 | func strokeWidth(_ width: CGFloat) -> CuteAttribute { 210 | ranges.forEach { 211 | base.addAttribute(.strokeWidth, value: width, range: $0) 212 | } 213 | return self 214 | } 215 | 216 | /// Set shadow for `NSShadowAttributeName`, 217 | /// this can be called after `range(_:)`, 218 | /// or not the range is default all the string. 219 | /// 220 | /// - Parameter shadow: NSShadow type without default value. 221 | /// - Returns: self 222 | func shadow(_ shadow: NSShadow) -> CuteAttribute { 223 | ranges.forEach { 224 | base.addAttribute(.shadow, value: shadow, range: $0) 225 | } 226 | return self 227 | } 228 | 229 | /// Set textEffect for `NSTextEffectAttributeName`, 230 | /// this can be called after `range(_:)`, 231 | /// or not the range is default all the string. 232 | /// 233 | /// - Parameter effect: String type without default value. 234 | /// - Returns: self 235 | func textEffect(_ effect: String) -> CuteAttribute { 236 | ranges.forEach { 237 | base.addAttribute(.textEffect, value: effect, range: $0) 238 | } 239 | return self 240 | } 241 | 242 | /// Set obliqueness for `NSObliquenessAttributeName`, 243 | /// this can be called after `range(_:)`, 244 | /// or not the range is default all the string. 245 | /// 246 | /// - Parameter value: CGFloat type without default value. 247 | /// - Returns: self 248 | func obliqueness(_ value: CGFloat) -> CuteAttribute { 249 | ranges.forEach { 250 | base.addAttribute(.obliqueness, value: value, range: $0) 251 | } 252 | return self 253 | } 254 | 255 | /// Set expansion for `NSExpansionAttributeName`, 256 | /// this can be called after `range(_:)`, 257 | /// or not the range is default all the string. 258 | /// 259 | /// - Parameter value: CGFloat type without default value. 260 | /// - Returns: self 261 | func expansion(_ value: CGFloat) -> CuteAttribute { 262 | ranges.forEach { 263 | base.addAttribute(.expansion, value: value, range: $0) 264 | } 265 | return self 266 | } 267 | 268 | /// Set textAttachment for `NSAttachmentAttributeName`, 269 | /// this can be called after `range(_:)`, or not the range is default all the string. 270 | /// 271 | /// - Parameter value: NSTextAttachment type without default value. 272 | /// - Returns: self 273 | func textAttachment(_ value: NSTextAttachment) -> CuteAttribute { 274 | ranges.forEach { 275 | base.addAttribute(.attachment, value: value, range: $0) 276 | } 277 | return self 278 | } 279 | 280 | /// Set `NSMutableParagraphStyle` 281 | /// 282 | /// - Parameter value: NSMutableParagraphStyle type, including lineSpacing, lineBreakMode... 283 | /// - Returns: self 284 | func paragraphStyle(_ value: NSMutableParagraphStyle) -> CuteAttribute { 285 | ranges.forEach { 286 | base.addAttribute(.paragraphStyle, value: value, range: $0) 287 | } 288 | return self 289 | } 290 | 291 | /// Set the whole range lineSpace, it will come into effect when `matchAll()`, 292 | /// 293 | /// - Parameter value: line space value 294 | /// - Returns: self 295 | func lineSpacing(_ value: CGFloat) -> CuteAttribute { 296 | let paragraphStyle = NSMutableParagraphStyle() 297 | paragraphStyle.lineSpacing = value 298 | return self.paragraphStyle(paragraphStyle) 299 | } 300 | } 301 | -------------------------------------------------------------------------------- /CuteAttribute/CuteAttribute+Color.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CuteAttribute+Color.swift 3 | // Cute 4 | // 5 | // Created by vsccw on 2017/8/13. 6 | // Copyright © 2017年 https://vsccw.com. All rights reserved. 7 | // 8 | 9 | import UIKit.UIColor 10 | 11 | public extension CuteAttribute where Base: NSMutableAttributedString { 12 | 13 | /// Set `NSForegroundColorAttributeName` to red. 14 | var red: CuteAttribute { 15 | return color(UIColor.red) 16 | } 17 | 18 | /// Set `NSForegroundColorAttributeName` to blue. 19 | var blue: CuteAttribute { 20 | return color(UIColor.blue) 21 | } 22 | 23 | /// Set `NSForegroundColorAttributeName` to black. 24 | var black: CuteAttribute { 25 | return color(UIColor.black) 26 | } 27 | 28 | /// Set `NSForegroundColorAttributeName` to green. 29 | var green: CuteAttribute { 30 | return color(UIColor.green) 31 | } 32 | 33 | /// Set `NSForegroundColorAttributeName` to cyan. 34 | var cyan: CuteAttribute { 35 | return color(UIColor.cyan) 36 | } 37 | 38 | /// Set `NSForegroundColorAttributeName` to yellow. 39 | var yellow: CuteAttribute { 40 | return color(UIColor.yellow) 41 | } 42 | 43 | /// Set `NSForegroundColorAttributeName` to magenta. 44 | var magenta: CuteAttribute { 45 | return color(UIColor.magenta) 46 | } 47 | 48 | /// Set `NSForegroundColorAttributeName` to gray. 49 | var gray: CuteAttribute { 50 | return color(UIColor.gray) 51 | } 52 | 53 | /// Set `NSForegroundColorAttributeName` to white. 54 | var white: CuteAttribute { 55 | return color(UIColor.white) 56 | } 57 | 58 | /// Set `NSForegroundColorAttributeName` to lightGray. 59 | var lightGray: CuteAttribute { 60 | return color(UIColor.lightGray) 61 | } 62 | 63 | /// Set `NSForegroundColorAttributeName` to darkGray. 64 | var darkGray: CuteAttribute { 65 | return color(UIColor.darkGray) 66 | } 67 | 68 | /// Set `NSForegroundColorAttributeName` to purple. 69 | var purple: CuteAttribute { 70 | return color(UIColor.purple) 71 | } 72 | 73 | /// Set `NSForegroundColorAttributeName` to orange. 74 | var orange: CuteAttribute { 75 | return color(UIColor.orange) 76 | } 77 | 78 | /// Set `NSForegroundColorAttributeName` to brown. 79 | var brown: CuteAttribute { 80 | return color(UIColor.brown) 81 | } 82 | 83 | /// Set `NSForegroundColorAttributeName` to clear. 84 | var clear: CuteAttribute { 85 | return color(UIColor.clear) 86 | } 87 | 88 | /// Set `NSForegroundColorAttributeName` to rgb color. 89 | /// 90 | /// - Parameter hex: Int value, example: 0xffbb00 91 | /// - Returns: self 92 | func rgbColor(_ hex: Int) -> CuteAttribute { 93 | let rgb = UIColor(rgb: hex) 94 | return color(rgb) 95 | } 96 | 97 | /// Set `NSForegroundColorAttributeName` to argb color. 98 | /// 99 | /// - Parameter hex: Int value, example: 0xbbffddaa 100 | /// - Returns: self 101 | func argbColor(_ hex: Int) -> CuteAttribute { 102 | let argb = UIColor(argb: hex) 103 | return color(argb) 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /CuteAttribute/CuteAttribute+Convert.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CuteAttribute+Convert.swift 3 | // Cute 4 | // 5 | // Created by vsccw on 2017/8/10. 6 | // Copyright © 2017年 https://vsccw.com. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension CuteAttribute { 12 | /// `String`, `NSString`, `NSMutableAttributedString` or `NSAttributedString` to `NSMutableAttributedString` 13 | /// 14 | /// - Parameter str: Any value. 15 | /// - Returns: NSMutableAttributedString 16 | static func convertToMutableAttributedString(_ str: Any) -> NSMutableAttributedString { 17 | let isValid = (str is String) 18 | || (str is NSString) 19 | || (str is NSAttributedString) 20 | || (str is NSMutableAttributedString) 21 | 22 | assert(isValid, "only support `String`, `NSString`, `NSAttributedString`, `NSMutableAttributedString`.") 23 | 24 | if let attribuedString = str as? NSMutableAttributedString { 25 | return attribuedString 26 | } else if let attribuedString = str as? NSAttributedString { 27 | return NSMutableAttributedString(attributedString: attribuedString) 28 | } else if let string = str as? NSString { 29 | return NSMutableAttributedString(string: string as String) 30 | } else if let string = str as? String { 31 | return NSMutableAttributedString(string: string) 32 | } else { 33 | return NSMutableAttributedString(string: "") 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /CuteAttribute/CuteAttribute+Match.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CuteAttribute+Match.swift 3 | // Cute 4 | // 5 | // Created by vsccw on 2017/8/11. 6 | // Copyright © 2017年 https://vsccw.com. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension CuteAttribute where Base: NSMutableAttributedString { 12 | 13 | /// As set `NSRange`'s location, it must less than string's length. 14 | /// 15 | /// - Parameter location: the location you choose to set. 16 | /// - Returns: self 17 | func from(_ location: Int) -> CuteAttribute { 18 | assert(location <= base.string.length, "`from` must less than string's length.") 19 | from = location 20 | return self 21 | } 22 | 23 | /// As set `NSRange`'s length, 24 | /// it must less than string's length, 25 | /// and it must more than from. It must be work with `from(_:)` 26 | /// 27 | /// - Parameter location: the locatoin you choose to set. 28 | /// - Returns: self 29 | func to(_ location: Int) -> CuteAttribute { 30 | assert(location <= base.string.length, "`to` must less than string's length.") 31 | let range = NSRange(location: from, length: location - from) 32 | return self.range(range) 33 | } 34 | 35 | /// Match all the `NSMutableAttributedString`. 36 | /// 37 | /// - Returns: self 38 | func matchAll() -> CuteAttribute { 39 | self.ranges = [base.string.nsrange] 40 | return self 41 | } 42 | 43 | /// Match `subString` with regex pattern. 44 | /// 45 | /// - Parameter re: the regex pattern you set. 46 | /// - Returns: self 47 | func match(using regex: String) -> CuteAttribute { 48 | do { 49 | let regex = try RegexHelper(pattern: regex) 50 | ranges = regex.matchs(input: base.string) 51 | } catch let err { 52 | fatalError("could not find string with re: \(err)") 53 | } 54 | return self 55 | } 56 | 57 | /// Match all the subString. 58 | /// 59 | /// - Parameter str: subString 60 | /// - Returns: self 61 | func match(string: String) -> CuteAttribute { 62 | var range = base.string.range(substring: string) 63 | assert(range.location != NSNotFound, "Substring must be in string.") 64 | ranges.removeAll() 65 | ranges.append(range) 66 | repeat { 67 | let location = range.location + range.length 68 | let length = base.string.length - location 69 | let allRange = NSRange(location: location, length: length) 70 | range = base.string.range(substring: string, options: [], range: allRange) 71 | if range.length == 0 || range.location == NSNotFound { 72 | break 73 | } 74 | ranges.append(range) 75 | } while true 76 | return self 77 | } 78 | 79 | /// Match the special range 80 | /// 81 | /// - Parameter ran: the NSRange you set. 82 | /// - Returns: self 83 | func match(range ran: NSRange) -> CuteAttribute { 84 | assert(base.string.nsrange >> ran, "range should be in range of string.") 85 | return self.range(ran) 86 | } 87 | 88 | /// Match all the url. 89 | /// 90 | /// - Returns: self 91 | func matchAllURL() -> CuteAttribute { 92 | return matchAllAttribute(checkingType: .link) 93 | } 94 | 95 | /// Match all the phone number. 96 | /// 97 | /// - Returns: self 98 | func matchAllPhoneNumber() -> CuteAttribute { 99 | return matchAllAttribute(checkingType: .phoneNumber) 100 | } 101 | 102 | /// Match all the address. 103 | /// 104 | /// - Returns: self 105 | func matchAllAddress() -> CuteAttribute { 106 | return matchAllAttribute(checkingType: .address) 107 | } 108 | 109 | /// Match all the date. 110 | /// 111 | /// - Returns: self 112 | func matchAllDate() -> CuteAttribute { 113 | return matchAllAttribute(checkingType: .date) 114 | } 115 | 116 | internal func matchAllAttribute(checkingType: NSTextCheckingResult.CheckingType) -> CuteAttribute { 117 | do { 118 | let dataHelper = try DataDetectorHelper(types: checkingType.rawValue) 119 | ranges = dataHelper.matches(string: base.string) 120 | } catch let err { 121 | fatalError("Could not match string : \(err)") 122 | } 123 | return self 124 | } 125 | 126 | internal var from: Int { 127 | set { 128 | objc_setAssociatedObject(base, CuteAttributeKey.fromKey, Box(newValue), .OBJC_ASSOCIATION_RETAIN_NONATOMIC) 129 | } 130 | get { 131 | return (objc_getAssociatedObject(base, CuteAttributeKey.fromKey) as? Box)?.value ?? 0 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /CuteAttribute/CuteAttribute+NSRange.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CuteAttribute+NSRange.swift 3 | // Cute 4 | // 5 | // Created by vsccw on 2017/8/11. 6 | // Copyright © 2017年 https://vsccw.com. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension CuteAttribute where Base: NSMutableAttributedString { 12 | 13 | /// Set the range. 14 | /// 15 | /// - Parameter range: NSRange value. 16 | /// - Returns: self 17 | func range(_ range: NSRange) -> CuteAttribute { 18 | assert(base.string.nsrange >> range, "range should be in range of string.") 19 | self.ranges = [range] 20 | return self 21 | } 22 | 23 | internal(set) var ranges: [NSRange] { 24 | get { 25 | let defaultRange = NSRange(location: 0, length: base.length) 26 | let value = (objc_getAssociatedObject(base, CuteAttributeKey.rangesKey) as? Box<[NSRange]>)?.value 27 | return value ?? [defaultRange] 28 | } 29 | set { 30 | let value = Box(newValue) 31 | objc_setAssociatedObject(base, CuteAttributeKey.rangesKey, value, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) 32 | } 33 | } 34 | 35 | /// Set the ranges. 36 | /// 37 | /// - Parameter ranges: [NSRange] value. 38 | /// - Returns: self 39 | func ranges(_ ranges: [NSRange]) -> CuteAttribute { 40 | let isValid = ranges 41 | .compactMap { return base.string.nsrange >> $0 } 42 | .reduce(true) { return $1 && $0 } 43 | assert(isValid, "ranges must in string.") 44 | self.ranges = ranges 45 | return self 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /CuteAttribute/CuteAttribute+Tap.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CuteAttribute+Tap.swift 3 | // CuteAttribute 4 | // 5 | // Created by vsccw on 2017/10/8. 6 | // Copyright © 2017年 https://vsccw.com. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// the tap type matched when you want to use `TapableLabel`. 12 | /// 13 | /// - link: url link match, eg: `https://blog.vsccw.com` 14 | /// - phoneNumber: phone number match, eg: `+8617600636630` 15 | /// - custom: custom string match, including regux and origin string, eg: `vsccw`, `^[A-Za-z]+$`. 16 | public enum TapType { 17 | case link 18 | case phoneNumber 19 | case custom(TapCustom) 20 | } 21 | 22 | /// use this to custom tap string. regex or string. 23 | /// 24 | /// - regex: the regex to match 25 | /// - origin: the origin string to match 26 | public enum TapCustom { 27 | case regex(String) 28 | case origin(String) 29 | } 30 | 31 | extension CuteAttribute where Base: NSMutableAttributedString { 32 | 33 | /// Set tap action for `UILabel`. 34 | /// 35 | /// - Parameter type: CuteAttributeTapType without default value. 36 | /// - Returns: self 37 | public func tap(_ type: TapType) -> CuteAttribute { 38 | let tapRanges: [NSRange] 39 | switch type { 40 | case .link: 41 | tapRanges = rangesFrom(checkingType: .link) 42 | case .phoneNumber: 43 | tapRanges = rangesFrom(checkingType: .phoneNumber) 44 | case .custom(let tapCustom): 45 | switch tapCustom { 46 | case .origin(let originString): 47 | tapRanges = rangeFrom(string: originString) 48 | case .regex(let regexString): 49 | tapRanges = rangesFrom(string: regexString) 50 | } 51 | } 52 | if self.tapRanges.isEmpty { 53 | self.tapRanges = tapRanges 54 | } else { 55 | self.tapRanges.append(contentsOf: tapRanges) 56 | } 57 | 58 | return self 59 | } 60 | 61 | /// Set highlight textColor for `UILabel`. 62 | /// 63 | /// - Parameter highlight: CuteHighlight , default value is `UIColor.gray`. 64 | /// - Returns: self 65 | public func highlight(_ highlight: CuteHighlight = .default) -> CuteAttribute { 66 | self.labelHighlight = highlight 67 | return self 68 | } 69 | 70 | /// get ranges from `NSTextCheckingResult.CheckingType`, including link or phoneNumber. 71 | /// 72 | /// - Parameter checkingType: NSTextCheckingResult.CheckingType. 73 | /// - Returns: [NSRange] 74 | internal func rangesFrom(checkingType: NSTextCheckingResult.CheckingType) -> [NSRange] { 75 | do { 76 | let dataHelper = try DataDetectorHelper(types: checkingType.rawValue) 77 | return dataHelper.matches(string: base.string) 78 | } catch { 79 | return [] 80 | } 81 | } 82 | 83 | /// get ranges from special string. 84 | /// 85 | /// - Parameter str: String 86 | /// - Returns: [NSRange] 87 | internal func rangeFrom(string str: String) -> [NSRange] { 88 | var range = base.string.range(substring: str) 89 | var tapRanges: [NSRange] = [] 90 | assert(range.location != NSNotFound, "Substring must be in string.") 91 | tapRanges.append(range) 92 | repeat { 93 | let location = range.location + range.length 94 | let length = base.string.length - location 95 | let allRange = NSRange(location: location, length: length) 96 | range = base.string.range(substring: str, options: [], range: allRange) 97 | if range.length == 0 || range.location == NSNotFound { 98 | break 99 | } 100 | tapRanges.append(range) 101 | } while true 102 | return tapRanges 103 | } 104 | 105 | /// get ranges from regex string. 106 | /// 107 | /// - Parameter string: regex string 108 | /// - Returns: [NSRange] 109 | internal func rangesFrom(string: String) -> [NSRange] { 110 | do { 111 | let regex = try RegexHelper(pattern: string) 112 | return regex.matchs(input: base.string) 113 | } catch { 114 | return [] 115 | } 116 | } 117 | 118 | /// the special tapRanges array. 119 | internal var tapRanges: [NSRange] { 120 | get { 121 | let value = (objc_getAssociatedObject(base, CuteAttributeKey.tapRangesKey) 122 | as? Box<[NSRange]>)?.value 123 | return value ?? [] 124 | } 125 | set { 126 | objc_setAssociatedObject(base, 127 | CuteAttributeKey.tapRangesKey, 128 | Box(newValue), 129 | .OBJC_ASSOCIATION_RETAIN_NONATOMIC) 130 | } 131 | } 132 | 133 | /// UILabel highlight object if set. 134 | internal var labelHighlight: CuteHighlight? { 135 | get { 136 | return (objc_getAssociatedObject(base, CuteAttributeKey.highlightKey) as? Box)?.value 137 | } 138 | 139 | set { 140 | objc_setAssociatedObject(base, 141 | CuteAttributeKey.highlightKey, 142 | Box(newValue), 143 | .OBJC_ASSOCIATION_RETAIN_NONATOMIC) 144 | } 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /CuteAttribute/CuteAttribute+Underline.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CuteAttribute+Underline.swift 3 | // CuteAttribute 4 | // 5 | // Created by vsccw on 2017/8/13. 6 | // Copyright © 2017年 https://vsccw.com. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension CuteAttribute where Base: NSMutableAttributedString { 12 | /// `NSUnderlineStyle.styleSingle` underline style 13 | var singleUnderline: CuteAttribute { 14 | return underline(.single) 15 | } 16 | 17 | /// `NSUnderlineStyle.styleNone` underline style 18 | var noneUnderline: CuteAttribute { 19 | return underline([]) 20 | } 21 | 22 | /// `NSUnderlineStyle.styleThick` underline style 23 | var thickUnderline: CuteAttribute { 24 | return underline(.thick) 25 | } 26 | 27 | /// `NSUnderlineStyle.styleDouble` underline style 28 | var doubleUnderline: CuteAttribute { 29 | return underline(.double) 30 | } 31 | 32 | /// `NSUnderlineStyle.patternDot` underline style 33 | var patternDotUnderline: CuteAttribute { 34 | return underline(.patternDot) 35 | } 36 | 37 | /// `NSUnderlineStyle.patternDash` underline style 38 | var patternDashUnderline: CuteAttribute { 39 | return underline(.patternDash) 40 | } 41 | 42 | /// `NSUnderlineStyle.patternDashDot` underline style 43 | var patternDashDotUnderline: CuteAttribute { 44 | return underline(.patternDashDot) 45 | } 46 | 47 | /// `NSUnderlineStyle.patternDashDotDot` underline style 48 | var patternDashDotDotUnderline: CuteAttribute { 49 | return underline(.patternDashDotDot) 50 | } 51 | 52 | /// `NSUnderlineStyle.byWord` underline style 53 | var byWordUnderline: CuteAttribute { 54 | return underline(.byWord) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /CuteAttribute/CuteAttribute+append.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CuteAttribute+append.swift 3 | // CuteAttribute 4 | // 5 | // Created by vsccw on 2017/8/13. 6 | // Copyright © 2017年 https://vsccw.com. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension CuteAttribute where Base: NSMutableAttributedString { 12 | /// Append 13 | /// `String`, 14 | /// `NSString`, 15 | /// `NSMutableAttributedString` 16 | /// or `NSAttributedString` to the base `NSMutableAttributedString` 17 | /// 18 | /// - Parameter string: It can be `String`, `NSString`, `NSMutableAttributedString` or `NSAttributedString` 19 | /// - Returns: self 20 | func append(_ string: Any) -> CuteAttribute { 21 | let attributedString = CuteAttribute.convertToMutableAttributedString(string) 22 | base.append(attributedString) 23 | return self 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /CuteAttribute/CuteAttribute.h: -------------------------------------------------------------------------------- 1 | // 2 | // CuteAttribute.h 3 | // CuteAttribute 4 | // 5 | // Created by vsccw on 2017/8/13. 6 | // Copyright © 2017年 https://vsccw.com. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for CuteAttribute. 12 | FOUNDATION_EXPORT double CuteAttributeVersionNumber; 13 | 14 | //! Project version string for CuteAttribute. 15 | FOUNDATION_EXPORT const unsigned char CuteAttributeVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /CuteAttribute/CuteAttribute.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CuteAttribute.swift 3 | // Cute 4 | // 5 | // Created by vsccw on 2017/8/9. 6 | // Copyright © 2017年 https://vsccw.com. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// A protocol type for `CuteAttribute` 12 | public protocol CuteAttributeable { 13 | 14 | associatedtype Attributeable 15 | var cute: Attributeable { get } 16 | } 17 | 18 | public final class CuteAttribute: NSObject { 19 | 20 | public let base: Base 21 | public init(_ base: Base) { 22 | self.base = base 23 | } 24 | } 25 | 26 | public extension CuteAttributeable { 27 | 28 | var cute: CuteAttribute { 29 | get { return CuteAttribute(self) } 30 | set { _ = newValue } 31 | } 32 | } 33 | 34 | extension NSMutableAttributedString: CuteAttributeable { } 35 | 36 | public extension CuteAttribute where Base: NSMutableAttributedString { 37 | 38 | var attributedString: NSMutableAttributedString { 39 | return base 40 | } 41 | 42 | var copy: CuteAttribute? { 43 | return type(of: self).init(base) as? CuteAttribute 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /CuteAttribute/CuteAttributeKeys.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CuteAttributeKeys.swift 3 | // Cute 4 | // 5 | // Created by vsccw on 2017/8/9. 6 | // Copyright © 2017年 https://vsccw.com. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | internal struct CuteAttributeKey { 12 | 13 | static let rangesKey = UnsafeRawPointer(bitPattern: "rangesKey".hashValue)! 14 | 15 | static let fromKey = UnsafeRawPointer(bitPattern: "fromKey".hashValue)! 16 | 17 | static let tapRangesKey = UnsafeRawPointer(bitPattern: "tapRangesKey".hashValue)! 18 | 19 | static let viewCuteKey = UnsafeRawPointer(bitPattern: "labelCuteKey".hashValue)! 20 | 21 | static let highlightKey = UnsafeRawPointer(bitPattern: "highlightKey".hashValue)! 22 | } 23 | -------------------------------------------------------------------------------- /CuteAttribute/CuteHighlight.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CuteHighlight.swift 3 | // CuteAttribute 4 | // 5 | // Created by vsccw on 2018/1/13. 6 | // Copyright © 2018年 https://vsccw.com. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | public struct CuteHighlight { 13 | /// default highlight text color is `UIColor.gray` 14 | public static let `default` = CuteHighlight(textColor: .gray) 15 | /// the text color for UILabel highlight status. 16 | public let textColor: UIColor 17 | /// the init function for `CuteHighlight`, 18 | /// if you want to use it, you must use `TapableLabel`. 19 | /// 20 | /// - Parameter textColor: apply to UILabel's text color. 21 | public init(textColor: UIColor) { 22 | self.textColor = textColor 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /CuteAttribute/DataDetectorHelper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DataDetectorHelper.swift 3 | // Cute 4 | // 5 | // Created by vsccw on 2017/8/12. 6 | // Copyright © 2017年 https://vsccw.com. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public struct DataDetectorHelper { 12 | let dataDetactor: NSDataDetector 13 | 14 | public init(types: NSTextCheckingTypes) throws { 15 | try dataDetactor = NSDataDetector(types: types) 16 | } 17 | 18 | public func matches(string: String) -> [NSRange] { 19 | let range = NSRange(location: 0, length: string.length) 20 | return dataDetactor.matches(in: string, options: [], range: range) 21 | .filter({ $0.range.length > 0 }) 22 | .compactMap({ $0.range }) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /CuteAttribute/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.2.5 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /CuteAttribute/RegexHelper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RegexHelper.swift 3 | // Cute 4 | // 5 | // Created by vsccw on 2017/8/12. 6 | // Copyright © 2017年 https://vsccw.com. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public struct RegexHelper { 12 | 13 | let regex: NSRegularExpression 14 | 15 | public init(pattern: String) throws { 16 | try regex = NSRegularExpression(pattern: pattern, options: .allowCommentsAndWhitespace) 17 | } 18 | 19 | public func matchs(input: String) -> [NSRange] { 20 | let matches = regex.matches(in: input, 21 | options: [], 22 | range: NSRange(location: 0, length: input.utf16.count)) 23 | 24 | return matches 25 | .compactMap({ $0.range }) 26 | .filter({ $0.length > 0 }) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /CuteAttribute/String+Cute.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+Cute.swift 3 | // Cute 4 | // 5 | // Created by vsccw on 2017/8/10. 6 | // Copyright © 2017年 https://vsccw.com. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | infix operator >> 12 | public func >> (left: NSRange, right: NSRange) -> Bool { 13 | return right.location >= left.location 14 | && (left.location + left.length) >= (right.location + right.length) 15 | } 16 | 17 | public extension NSAttributedString { 18 | 19 | var cute: CuteAttribute { 20 | return CuteAttribute(NSMutableAttributedString(attributedString: self)) 21 | } 22 | } 23 | 24 | public extension NSString { 25 | 26 | var range: NSRange { 27 | return NSRange(location: 0, length: length) 28 | } 29 | 30 | var string: String { 31 | return self as String 32 | } 33 | 34 | var cute: CuteAttribute { 35 | return CuteAttribute(NSMutableAttributedString(string: string)) 36 | } 37 | } 38 | 39 | public extension String { 40 | 41 | var nsstring: NSString { 42 | return self as NSString 43 | } 44 | 45 | var cute: CuteAttribute { 46 | return CuteAttribute(NSMutableAttributedString(string: self)) 47 | } 48 | 49 | var length: Int { 50 | return nsstring.length 51 | } 52 | 53 | var nsrange: NSRange { 54 | return NSRange(location: 0, length: nsstring.length) 55 | } 56 | 57 | func range(substring: String) -> NSRange { 58 | return nsstring.range(of: substring) 59 | } 60 | 61 | func range(substring: String, options: NSString.CompareOptions, range: NSRange) -> NSRange { 62 | return nsstring.range(of: substring, options: options, range: range) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /CuteAttribute/TapableLabel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TapableLabel.swift 3 | // CuteAttribute 4 | // 5 | // Created by vsccw on 2017/11/12. 6 | // Copyright © 2017年 https://vsccw.com. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | /// The delegate to handle tap of label. 12 | @objc 13 | public protocol TapableLabelDelegate: AnyObject { 14 | @objc 15 | optional func tapableLabel(_ label: TapableLabel, didTap range: NSRange, text: String?) 16 | } 17 | 18 | /// a subclass UILabel used to handle tap with CuteAttribute's `tap(_ type: CuteAttributeTapType)` 19 | @IBDesignable 20 | open class TapableLabel: UILabel { 21 | 22 | public weak var delegate: TapableLabelDelegate? 23 | 24 | private var tappingRange: NSRange? 25 | private var highlight: CuteHighlight? 26 | private var previousAttributes: Box?>? 27 | 28 | public override init(frame: CGRect) { 29 | super.init(frame: frame) 30 | commitInit() 31 | } 32 | 33 | public required init?(coder aDecoder: NSCoder) { 34 | super.init(coder: aDecoder) 35 | commitInit() 36 | } 37 | 38 | open override func awakeFromNib() { 39 | super.awakeFromNib() 40 | commitInit() 41 | } 42 | 43 | open override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { 44 | let superHitTest = super.hitTest(point, with: event) 45 | guard bounds.contains(point) else { return superHitTest } 46 | guard let tapRanges = cute.attributedText?.tapRanges else { return superHitTest } 47 | guard didTapRangeOfLink(inRanges: tapRanges, tapLocation: point) != nil else { return superHitTest } 48 | return self 49 | } 50 | 51 | open override func touchesBegan(_ touches: Set, with event: UIEvent?) { 52 | guard let touch = touches.first else { 53 | super.touchesBegan(touches, with: event) 54 | return 55 | } 56 | let location = touch.location(in: self) 57 | guard bounds.contains(location) else { 58 | super.touchesBegan(touches, with: event) 59 | return 60 | } 61 | guard let tapRanges = cute.attributedText?.tapRanges else { 62 | super.touchesBegan(touches, with: event) 63 | return 64 | } 65 | guard let tappedRange = didTapRangeOfLink(inRanges: tapRanges, tapLocation: location) else { 66 | super.touchesBegan(touches, with: event) 67 | return 68 | } 69 | tappingRange = tappedRange 70 | let attriubes = attributedText?.attributes(at: tappedRange.location, 71 | longestEffectiveRange: nil, 72 | in: tappedRange) 73 | let attributedColor = attriubes?[.foregroundColor] as? UIColor 74 | highlight = CuteHighlight(textColor: attributedColor ?? textColor) 75 | let highlightColor = cute.attributedText?.labelHighlight?.textColor ?? CuteHighlight.default.textColor 76 | cute.attributedText = cute.attributedText? 77 | .match(range: tappedRange) 78 | .color(highlightColor) 79 | } 80 | 81 | open override func touchesEnded(_ touches: Set, with event: UIEvent?) { 82 | super.touchesEnded(touches, with: event) 83 | if let tappingRange = self.tappingRange { 84 | delegate?.tapableLabel?(self, 85 | didTap: tappingRange, 86 | text: text?.nsstring.substring(with: tappingRange)) 87 | let textColor = highlight?.textColor ?? .clear 88 | cute.attributedText = cute.attributedText? 89 | .match(range: tappingRange) 90 | .color(textColor) 91 | } 92 | 93 | tappingRange = nil 94 | highlight = nil 95 | previousAttributes = nil 96 | } 97 | 98 | open override func touchesCancelled(_ touches: Set, with event: UIEvent?) { 99 | super.touchesCancelled(touches, with: event) 100 | if let tappingRange = self.tappingRange { 101 | let textColor = highlight?.textColor ?? .clear 102 | cute.attributedText = cute.attributedText? 103 | .match(range: tappingRange) 104 | .color(textColor) 105 | } 106 | 107 | tappingRange = nil 108 | highlight = nil 109 | previousAttributes = nil 110 | } 111 | 112 | internal func commitInit() { 113 | isUserInteractionEnabled = true 114 | } 115 | } 116 | 117 | extension TapableLabel { 118 | 119 | internal func didTapRangeOfLink(inRanges ranges: [NSRange]?, tapLocation: CGPoint) -> NSRange? { 120 | guard let ranges = ranges, let text = self.text else { return nil } 121 | 122 | let attributedString = NSMutableAttributedString(string: text) 123 | let textRange = NSRange(location: 0, length: attributedString.length) 124 | if let font = self.font { 125 | attributedString.addAttributes([.font: font], range: textRange) 126 | } 127 | let paragraphStyle = NSMutableParagraphStyle() 128 | paragraphStyle.alignment = self.textAlignment 129 | 130 | if !text.isEmpty { 131 | let attrs = attributedText?.attributes(at: 0, effectiveRange: nil) 132 | if let paragraph = attrs?[NSAttributedString.Key.paragraphStyle] as? NSMutableParagraphStyle { 133 | paragraphStyle.lineSpacing = paragraph.lineSpacing 134 | } 135 | } 136 | attributedString.addAttributes([.paragraphStyle: paragraphStyle], range: textRange) 137 | 138 | let size = self.bounds.size 139 | let layoutManager = NSLayoutManager() 140 | let textContainer = NSTextContainer(size: CGSize.zero) 141 | let textStorage = NSTextStorage(attributedString: attributedString) 142 | 143 | layoutManager.addTextContainer(textContainer) 144 | textStorage.addLayoutManager(layoutManager) 145 | 146 | textContainer.lineFragmentPadding = 0.0 147 | textContainer.lineBreakMode = self.lineBreakMode 148 | textContainer.maximumNumberOfLines = self.numberOfLines 149 | textContainer.size = CGSize(width: size.width, height: (size.height + CGFloat(self.numberOfLines))) 150 | 151 | let boundingBox = layoutManager.usedRect(for: textContainer) 152 | let originY = (size.height - boundingBox.height) * 0.5 - boundingBox.minY 153 | let location = CGPoint(x: tapLocation.x, y: tapLocation.y - originY) 154 | guard location.x >= boundingBox.minX, 155 | location.x <= boundingBox.minX + boundingBox.width, 156 | location.y >= boundingBox.minY, 157 | location.y <= boundingBox.minY + boundingBox.height 158 | else { return nil } 159 | let characterIndex = layoutManager.glyphIndex(for: location, in: textContainer, 160 | fractionOfDistanceThroughGlyph: nil) 161 | return ranges.filter { NSLocationInRange(characterIndex, $0) }.first 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /CuteAttribute/UIColor+Cute.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIColor+Cute.swift 3 | // Cute 4 | // 5 | // Created by vsccw on 2017/8/13. 6 | // Copyright © 2017年 https://vsccw.com. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public extension UIColor { 12 | 13 | convenience init(rgb: Int) { 14 | self.init(red: CGFloat((rgb >> 16) & 0xff) / 255.0, 15 | green: CGFloat((rgb >> 8) & 0xff) / 255.0, 16 | blue: CGFloat(rgb & 0xff) / 255.0, 17 | alpha: 1.0) 18 | } 19 | 20 | convenience init(argb: Int) { 21 | self.init(red: CGFloat((argb >> 16) & 0xff) / 255.0, 22 | green: CGFloat((argb >> 8) & 0xff) / 255.0, 23 | blue: CGFloat(argb & 0xff) / 255.0, 24 | alpha: CGFloat((argb >> 24) & 0xff) / 255.0) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /CuteAttribute/UILabel+Cute.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UILabel+Cute.swift 3 | // CuteAttribute 4 | // 5 | // Created by vsccw on 2017/8/27. 6 | // Copyright © 2017年 https://vsccw.com. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UILabel: CuteAttributeable { } 12 | 13 | public extension CuteAttribute where Base: UILabel { 14 | var attributedText: CuteAttribute? { 15 | set { 16 | base.attributedText = newValue?.attributedString 17 | base.internalCuteAttribute = newValue 18 | } 19 | get { 20 | return base.internalCuteAttribute 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /CuteAttribute/UITextField+Cute.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UITextField+Cute.swift 3 | // CuteAttribute 4 | // 5 | // Created by vsccw on 2017/8/27. 6 | // Copyright © 2017年 https://vsccw.com. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UITextField: CuteAttributeable { } 12 | 13 | public extension CuteAttribute where Base: UITextField { 14 | var attributedText: CuteAttribute? { 15 | set { 16 | base.attributedText = newValue?.attributedString 17 | base.internalCuteAttribute = newValue 18 | } 19 | get { 20 | return base.internalCuteAttribute 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /CuteAttribute/UITextView+Cute.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UITextView+Cute.swift 3 | // CuteAttribute 4 | // 5 | // Created by vsccw on 2017/8/27. 6 | // Copyright © 2017年 https://vsccw.com. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UITextView: CuteAttributeable { } 12 | 13 | public extension CuteAttribute where Base: UITextView { 14 | var attributedText: CuteAttribute? { 15 | set { 16 | base.attributedText = newValue?.attributedString 17 | base.internalCuteAttribute = newValue 18 | } 19 | get { 20 | return base.internalCuteAttribute 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /CuteAttribute/UIView+Cute.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+Cute.swift 3 | // CuteAttribute 4 | // 5 | // Created by vsccw on 2017/11/12. 6 | // Copyright © 2017年 https://vsccw.com. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIView { 12 | internal var internalCuteAttribute: CuteAttribute? { 13 | set { 14 | objc_setAssociatedObject(self, 15 | CuteAttributeKey.viewCuteKey, 16 | Box(newValue), 17 | .OBJC_ASSOCIATION_RETAIN_NONATOMIC) 18 | } 19 | get { 20 | return (objc_getAssociatedObject(self, CuteAttributeKey.viewCuteKey) 21 | as? Box?>)?.value 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /CuteAttributeTests/CuteAttributeTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CuteAttributeTests.swift 3 | // CuteAttributeTests 4 | // 5 | // Created by vsccw on 2017/8/13. 6 | // Copyright © 2017年 https://vsccw.com. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import CuteAttribute 11 | 12 | class CuteAttributeTests: XCTestCase { 13 | var text: String! 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | text = "hello CuteAttribute, made by vsccw, Copyright © 2017年 https://vsccw.com. " 18 | } 19 | 20 | func testMatch() { 21 | let cute = text.cute 22 | let attributedString1 = cute 23 | .matchAllURL() 24 | .red 25 | .underline(.single, color: UIColor.yellow) 26 | .attributedString 27 | let attributedString2 = cute 28 | .match(string: "t") 29 | .blue 30 | .underline(.byWord, color: UIColor.orange) 31 | .attributedString 32 | let msg = "attributedString1 should equal to attributedString2." 33 | XCTAssert(attributedString1.isEqual(to: attributedString2), msg) 34 | } 35 | 36 | func testAttributed() { 37 | let cute = text.cute 38 | let attributedString1 = cute 39 | .matchAllURL() 40 | .red 41 | .underline(.single, color: UIColor.yellow) 42 | let ranges = attributedString1.matchAllURL().ranges 43 | var underlineStyle: Int? 44 | if let range = ranges.first { 45 | let attributes = attributedString1.attributedString.attributes(at: range.location, effectiveRange: nil) 46 | underlineStyle = attributes[NSAttributedString.Key.underlineStyle] as? Int 47 | } 48 | XCTAssert(underlineStyle == NSUnderlineStyle.single.rawValue, "the color should be equal to UIColor.red.") 49 | } 50 | func testColor() { 51 | let cute = text.cute 52 | let cuteAttributedString = cute.matchAll().red 53 | let color = cuteAttributedString.attributedString 54 | .attribute(.foregroundColor, at: 0, effectiveRange: nil) as? UIColor 55 | XCTAssert(color!.isEqual(UIColor.red), "color should be equal to red.") 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /CuteAttributeTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Example/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Example 4 | // 5 | // Created by vsccw on 2017/8/13. 6 | // Copyright © 2017年 https://vsccw.com. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | func application(_ application: UIApplication, 16 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 17 | return true 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /Example/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ios-marketing", 45 | "size" : "1024x1024", 46 | "scale" : "1x" 47 | } 48 | ], 49 | "info" : { 50 | "version" : 1, 51 | "author" : "xcode" 52 | } 53 | } -------------------------------------------------------------------------------- /Example/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Example/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 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. 29 | 30 | 31 | 32 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /Example/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /Example/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // Example 4 | // 5 | // Created by vsccw on 2017/8/13. 6 | // Copyright © 2017年 https://vsccw.com. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CuteAttribute 11 | 12 | class ViewController: UIViewController { 13 | let text = """ 14 | hello world, 15 | 17600636630 16 | 2018-04-21 18:03:48 17 | https://blog.vsccw.com 18 | """ 19 | 20 | @IBOutlet weak var textView: UITextView! 21 | 22 | @IBOutlet weak var testLabel: TapableLabel! 23 | 24 | override func viewDidLoad() { 25 | super.viewDidLoad() 26 | 27 | let dateFont = UIFont(name: "Menlo", size: 24) ?? UIFont.systemFont(ofSize: 24) 28 | textView.cute.attributedText = text.cute 29 | .from(0) 30 | .to(10) 31 | .blue 32 | .font(UIFont.systemFont(ofSize: 40)) 33 | .matchAllPhoneNumber() 34 | .rgbColor(0x880011) 35 | .underline(.single) 36 | .underlineColor(.gray) 37 | .matchAllURL() 38 | .red 39 | .singleUnderline 40 | .underlineColor(.blue) 41 | .matchAllDate() 42 | .green 43 | .doubleUnderline 44 | .underlineColor(.orange) 45 | .font(dateFont) 46 | let cuteAttr = "请点击该链接:https://vsccw.com,17600636630😆😆😆😆😆😆" 47 | .cute 48 | .matchAllURL() 49 | .color(.red) 50 | .underline(NSUnderlineStyle.single) 51 | .matchAll() 52 | .lineSpacing(30) 53 | .tap(.link) 54 | .highlight(.default) 55 | .tap(.phoneNumber) 56 | .highlight(.default) 57 | 58 | testLabel.cute.attributedText = cuteAttr 59 | 60 | testLabel.delegate = self 61 | } 62 | private func showAlertController(_ message: String?) { 63 | let alertController = UIAlertController(title: "你点击了", message: message, preferredStyle: .alert) 64 | alertController.addAction(UIAlertAction(title: "我知道啦", style: .default, handler: nil)) 65 | present(alertController, animated: true, completion: nil) 66 | } 67 | } 68 | 69 | extension ViewController: TapableLabelDelegate { 70 | 71 | func tapableLabel(_ label: TapableLabel, didTap range: NSRange, text: String?) { 72 | showAlertController(text) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /Images/baselineOffset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/status302/CuteAttribute/936e20483cf9feeae135c01b7838fbe16505cbfa/Images/baselineOffset.png -------------------------------------------------------------------------------- /Images/color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/status302/CuteAttribute/936e20483cf9feeae135c01b7838fbe16505cbfa/Images/color.png -------------------------------------------------------------------------------- /Images/font.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/status302/CuteAttribute/936e20483cf9feeae135c01b7838fbe16505cbfa/Images/font.png -------------------------------------------------------------------------------- /Images/kern.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/status302/CuteAttribute/936e20483cf9feeae135c01b7838fbe16505cbfa/Images/kern.png -------------------------------------------------------------------------------- /Images/ligature.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/status302/CuteAttribute/936e20483cf9feeae135c01b7838fbe16505cbfa/Images/ligature.png -------------------------------------------------------------------------------- /Images/link.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/status302/CuteAttribute/936e20483cf9feeae135c01b7838fbe16505cbfa/Images/link.png -------------------------------------------------------------------------------- /Images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/status302/CuteAttribute/936e20483cf9feeae135c01b7838fbe16505cbfa/Images/logo.png -------------------------------------------------------------------------------- /Images/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/status302/CuteAttribute/936e20483cf9feeae135c01b7838fbe16505cbfa/Images/screenshot.png -------------------------------------------------------------------------------- /Images/shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/status302/CuteAttribute/936e20483cf9feeae135c01b7838fbe16505cbfa/Images/shadow.png -------------------------------------------------------------------------------- /Images/strikethrough.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/status302/CuteAttribute/936e20483cf9feeae135c01b7838fbe16505cbfa/Images/strikethrough.png -------------------------------------------------------------------------------- /Images/stroke.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/status302/CuteAttribute/936e20483cf9feeae135c01b7838fbe16505cbfa/Images/stroke.png -------------------------------------------------------------------------------- /Images/underline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/status302/CuteAttribute/936e20483cf9feeae135c01b7838fbe16505cbfa/Images/underline.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Qiun Cheng 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 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.1 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "CuteAttribute", 8 | platforms: [.iOS(.v8)], 9 | products: [ 10 | // Products define the executables and libraries produced by a package, and make them visible to other packages. 11 | .library( 12 | name: "CuteAttribute", 13 | targets: ["CuteAttribute"]), 14 | ], 15 | dependencies: [ 16 | // Dependencies declare other packages that this package depends on. 17 | ], 18 | targets: [ 19 | // Targets are the basic building blocks of a package. A target can define a module or a test suite. 20 | // Targets can depend on other targets in this package, and on products in packages which this package depends on. 21 | .target( 22 | name: "CuteAttribute", 23 | path: "CuteAttribute"), 24 | .testTarget( 25 | name: "CuteAttributeTests", 26 | dependencies: ["CuteAttribute"], 27 | path: "CuteAttributeTests"), 28 | ] 29 | ) 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![](https://github.com/qiuncheng/CuteAttribute/blob/master/Images/logo.png?raw=true) 2 | CuteAttribute 3 | === 4 | [![Build Status](https://travis-ci.org/qiuncheng/CuteAttribute.svg?branch=master)](https://travis-ci.org/qiuncheng/CuteAttribute) 5 | [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) 6 | [![pod version](https://img.shields.io/cocoapods/v/CuteAttribute.svg)](https://cocoapods.org/pods/CuteAttribute) 7 | [![](https://img.shields.io/badge/Swift-4.0-yellowgreen.svg)](https://github.com/qiuncheng/CuteAttribute) 8 | [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/qiuncheng/NoticeBar/master/LICENSE) 9 | 10 | An elegant way to deal with attributed string in swift. It's convenient to create attributed string by `.cute`, whatever from `String`, `NSString`, `NSAttributedString` or `NSMutableAttributedString`. It's so amazing that almost all methods support `chaining`. I create this project just because I met [Typeset](http://github.com/draveness/typeset) serveral days ago. But I found it's so hard to use by swift. So I made this. 11 | 12 | ![](https://github.com/qiuncheng/CuteAttribute/blob/master/Images/screenshot.png?raw=true) 13 | 14 | Support 15 | === 16 | Swift 3.0 & iOS 8+ 17 | 18 | Installation 19 | === 20 | #### Swift Package Manager 21 | - add it to your Xcode project. 22 | - Import the header file in your project. 23 | 24 | ```swift 25 | import CuteAttribute 26 | ``` 27 | 28 | #### CocoaPods 29 | - add it to your Podfile. 30 | 31 | ```ruby 32 | pod 'CuteAttribute' 33 | ``` 34 | - Then run the command. 35 | 36 | ```shell 37 | pod install 38 | ``` 39 | - Import the header file in your project. 40 | 41 | ```swift 42 | import CuteAttribute 43 | ``` 44 | 45 | #### Carthage 46 | - Add CuteAttribute to your Cartfile. 47 | 48 | ```ruby 49 | github "qiuncheng/CuteAttribute" 50 | ``` 51 | - Run the command 52 | 53 | ```shell 54 | carthage update --platform ios 55 | ``` 56 | - Follow the rest of the [standard Carthage installation](https://github.com/Carthage/Carthage#adding-frameworks-to-an-application) instructions to add CuteAttribute to your project. 57 | 58 | - Import the header file in your project. 59 | 60 | ```swift 61 | import CuteAttribute 62 | ``` 63 | 64 | #### Manually 65 | - [Download](https://github.com/qiuncheng/CuteAttribute/archive/master.zip) the full file. 66 | - Drag the CuteAttribute folder to your project. 67 | 68 | Usage 69 | === 70 | ### Comparison 71 | attribute name | result | NSAttributedString | CuteAttribute 72 | --- | --- | --- | --- 73 | color | ![](https://github.com/qiuncheng/CuteAttribute/blob/master/Images/color.png?raw=true) | NSMutableAttributedString(string: "hello world", attributes: [.foregroundColor: UIColor.red]) | "hello world".cute.matchAll().color(.red) 74 | baselineOffset | ![](https://github.com/qiuncheng/CuteAttribute/blob/master/Images/baselineOffset.png?raw=true) | NSMutableAttributedString(string: "hello world", attributes: [.baselineOffset: 10]) | "hello world".cute.matchAll().baseline(10) 75 | underline | ![](https://github.com/qiuncheng/CuteAttribute/blob/master/Images/underline.png?raw=true) | NSMutableAttributedString(string: "hello world", attributes: [.underlineColor: UIColor.red, .underlineStyle: NSUnderlineStyle.styleSingle.rawValue]) | "hello world".cute.matchAll().underline(.styleSingle).underlineColor(.red) 76 | font | ![](https://github.com/qiuncheng/CuteAttribute/blob/master/Images/font.png?raw=true) | NSMutableAttributedString(string: "hello world", attributes: [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 20)]) | "hello world".cute.matchAll().font(UIFont.systemFont(ofSize: 20)) 77 | strikethrough | ![](https://github.com/qiuncheng/CuteAttribute/blob/master/Images/strikethrough.png?raw=true) | NSMutableAttributedString(string: "hello world", attributes: [.strikethroughStyle: NSUnderlineStyle.styleSingle.rawValue, .strikethroughColor: UIColor.red]) | "hello world".cute.matchAll().strikeThrough(.styleSingle).strokeColor(.red) 78 | link | ![](https://github.com/qiuncheng/CuteAttribute/blob/master/Images/link.png?raw=true) | NSMutableAttributedString(string: "hello world", attributes: [.link: "https://blog.vsccw.com"]) | "hello world".cute.matchAll().link("https://blog.vsccw.com") 79 | ligature | ![](https://github.com/qiuncheng/CuteAttribute/blob/master/Images/ligature.png?raw=true) | NSMutableAttributedString(string: "hello world", attributes: [.ligature: 1]) | "hello world".cute.matchAll().ligature(1) 80 | kern | ![](https://github.com/qiuncheng/CuteAttribute/blob/master/Images/kern.png?raw=true) | NSMutableAttributedString(string: "hello world", attributes: [.kern: 10]) | "hello world".cute.matchAll().kern(10) 81 | stroke | ![](https://github.com/qiuncheng/CuteAttribute/blob/master/Images/stroke.png?raw=true) | NSMutableAttributedString(string: "hello world", attributes: [.strokeColor: UIColor.red, .strokeWidth: 10]) | "hello world".cute.matchAll().strokeColor(.red).strokeWidth(10) 82 | shadow | `let shadow = NSShadow(); shadow.shadowColor = UIColor.red; shadow.shadowOffset = CGSize(width: 4, height: 4); shadow.shadowBlurRadius = 10;` ![](https://github.com/qiuncheng/CuteAttribute/blob/master/Images/shadow.png?raw=true) | NSMutableAttributedString(string: "hello world", attributes: [.shadow: shadow]) | "hello world".cute.matchAll().shadow(shadow) 83 | textEffect | ![no image](https://github.com/qiuncheng/CuteAttribute/blob/master/Images/textEffect.png?raw=true) | "hello world".cute.matchAll().textEffect("NSTextEffectLetterpressStyle") | "hello world".cute.matchAll().textEffect("NSTextEffectLetterpressStyle") 84 | obliqueness | ![](https://github.com/qiuncheng/CuteAttribute/blob/master/Images/obliqueness.png?raw=true) | NSMutableAttributedString(string: "hello world", attributes: [.obliqueness: 10]) | "hello world".cute.matchAll().obliqueness(10) 85 | expansion | ![](https://github.com/qiuncheng/CuteAttribute/blob/master/Images/expansion.png?raw=true) | NSMutableAttributedString(string: "hello world", attributes: [.expansion: 10]) | "hello world".cute.matchAll().expansion(10) 86 | textAttachment | `let attachment = NSTextAttachment(); attachment.image = UIImage(named: "hello.png");` ![](https://github.com/qiuncheng/CuteAttribute/blob/master/Images/textAttachment.png?raw=true) | NSMutableAttributedString(string: "hello world", attributes: [.attachment: attachment]) | "hello world".cute.matchAll().textAttachment(attachment) 87 | 88 | TODO 89 | === 90 | - [x] Documented. 91 | - [x] Example. 92 | - [ ] Test. 93 | - [ ] More convenience. 94 | 95 | Thanks 96 | === 97 | [Typeset](http://github.com/draveness/typeset) : Deal with AttributedString efficiently in `Objective-C`. 98 | 99 | LICENCE 100 | === 101 | Under [MIT](https://github.com/qiuncheng/CuteAttribute/blob/master/LICENSE) License 102 | --------------------------------------------------------------------------------