├── LICENSE ├── README.md ├── UILabel+YBAttributeTapAction-Demo.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── xcuserdata │ │ └── LYB.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── LYB.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ ├── UILabel+YBAttributeTapAction-Demo.xcscheme │ └── xcschememanagement.plist ├── UILabel+YBAttributeTapAction-Demo ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Contents.json │ ├── btn_sel.imageset │ │ ├── Contents.json │ │ ├── xgb_3_sel@2x.png │ │ └── xgb_3_sel@3x.png │ └── btn_unSel.imageset │ │ ├── Contents.json │ │ ├── xgb_3_unSel@2x.png │ │ └── xgb_3_unSel@3x.png ├── Home │ ├── HomeCell.h │ ├── HomeCell.m │ ├── HomeCell.xib │ ├── HomeTableViewController.h │ ├── HomeTableViewController.m │ └── HomeTableViewController.xib ├── Info.plist ├── Other │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── ViewController.h │ └── ViewController.m ├── Resources │ └── test_1.txt ├── YBAttributeTextTapAction │ ├── UILabel+YBAttributeTextTapAction.h │ └── UILabel+YBAttributeTextTapAction.m └── main.m ├── UILabel+YBAttributeTapAction-DemoTests ├── Info.plist └── UILabel_YBAttributeTapAction_DemoTests.m ├── UILabel+YBAttributeTapAction-DemoUITests ├── Info.plist └── UILabel_YBAttributeTapAction_DemoUITests.m ├── YBAttributeTextTapAction.podspec ├── YBAttributeTextTapAction ├── UILabel+YBAttributeTextTapAction.h └── UILabel+YBAttributeTextTapAction.m └── attributeAction.gif /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 yuanbo li 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # YBAttributeTextTapAction 2 | * 一行代码添加文本点击事件 3 | 4 | # 效果图 5 | ![(演示效果)](https://lyb5834.github.io/Images/attributeTapAction.gif) 6 | 7 | # Swfit版本(最新版还未更新,可直接集成OC版本) 8 | https://github.com/lyb5834/YBAttributeTextTapForSwfit.git 9 | 10 | # 使用方法 11 | * `#import "UILabel+YBAttributeTextTapAction.h"` 12 | * 先设置 `label.attributedText = ?????` 13 | * 有2种回调方法,第一种是用代理回调,第二种是用block回调 14 | * 代理回调 15 | * 1.传入要点击的字符串数组 16 | ``` 17 | [label yb_addAttributeTapActionWithStrings:@[@"xxx",@"xxx"] delegate:self]; 18 | ``` 19 | * 2.传入要点击的range数组 20 | ``` 21 | [label yb_addAttributeTapActionWithRanges:@[NSStringFromRange(range1),NSStringFromRange(range2)] delegate:self] 22 | ``` 23 | * block回调 24 | * 1.传入要点击的字符串数组 25 | ``` 26 | [label yb_addAttributeTapActionWithStrings:@[@"xxx",@"xxx"] tapClicked:^(UILabel *label,NSString *string, NSRange range,NSInteger index) { coding more... }]; 27 | ``` 28 | * 2.传入要点击的range数组 29 | ``` 30 | [label yb_addAttributeTapActionWithRanges:@[NSStringFromRange(range1),NSStringFromRange(range2)] tapClicked:^(UILabel *label,NSString *string, NSRange range,NSInteger index) { coding more... }]; 31 | ``` 32 | 33 | # CocoaPods支持 34 | * 只需在podfile中输入 `pod 'YBAttributeTextTapAction'` 即可 35 | 36 | # V3.0.3版本 37 | * 增加`yb_removeAttributeTapActions`方法,可以删除label上所有的附加点击事件,一般用于复用的cell,防止label上还存在不必要的点击事件 38 | 39 | # V3.0.2版本 40 | * 修复了在8.0系统上crash 的bug 41 | 42 | # V3.0.1版本 43 | * 修复字符串中有多个"\n"符号时计算不准确的bug 44 | 45 | # V3.0.0版本 46 | * 重构计算文字坐标的算法,点击准确率大大提升(再大的文本都不怕啦) 47 | * 重构API,回调参数更多 48 | * 增加传入range数组的API,可以指定range进行触发 49 | * 增加设置点击高亮色和是否扩大点击区域的API,麻麻再也不用担心我手指粗点不到啦 50 | * 重构demo,介绍更详细,用法更丰富 51 | * 修复一个页面多次调用会相互影响的bug 52 | * 修复在label上添加手势无效的bug 53 | 54 | # 问题总结 55 | * 因为UILabel的封装,有些属性不能实现,在此说一下一些提的比较多的问题 56 | 57 | ### 必须设置字体属性,不然点击范围会不准确,重要的事情说三遍 58 | ### 必须设置字体属性!必须设置字体属性!必须设置字体属性! 59 | 60 | eg: 61 | ``` 62 | [totalStr addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:orginFont] range:NSMakeRange(0, string.length)]; 63 | ``` 64 | * 关于文字排版的正确设置方式,设置`label.textAlignment = NSTextAlignmentCenter`会导致点击失效,正确的设置方法是 65 | ``` 66 | NSMutableParagraphStyle *sty = [[NSMutableParagraphStyle alloc] init]; 67 | sty.alignment = NSTextAlignmentCenter; 68 | [attributedString addAttribute:NSParagraphStyleAttributeName value:sty range:NSMakeRange(0, text.length)]; 69 | ``` 70 | 71 | # 版本支持 72 | * `xcode6.0+` 73 | 74 | * 如果您在使用本库的过程中发现任何bug或者有更好建议,欢迎[@issues](https://github.com/lyb5834/YBAttributeTextTapAction/issues) 或联系本人email lyb5834@126.com 75 | 76 | -------------------------------------------------------------------------------- /UILabel+YBAttributeTapAction-Demo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 0C3F7F291D65B59000FD1352 /* UILabel+YBAttributeTextTapAction.m in Sources */ = {isa = PBXBuildFile; fileRef = 0C3F7F281D65B59000FD1352 /* UILabel+YBAttributeTextTapAction.m */; }; 11 | 0C4B4EA21D264AEE00B3F1C4 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 0C4B4EA11D264AEE00B3F1C4 /* main.m */; }; 12 | 0C4B4EA51D264AEE00B3F1C4 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 0C4B4EA41D264AEE00B3F1C4 /* AppDelegate.m */; }; 13 | 0C4B4EA81D264AEE00B3F1C4 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0C4B4EA71D264AEE00B3F1C4 /* ViewController.m */; }; 14 | 0C4B4EAB1D264AEE00B3F1C4 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0C4B4EA91D264AEE00B3F1C4 /* Main.storyboard */; }; 15 | 0C4B4EAD1D264AEE00B3F1C4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0C4B4EAC1D264AEE00B3F1C4 /* Assets.xcassets */; }; 16 | 0C4B4EB01D264AEE00B3F1C4 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0C4B4EAE1D264AEE00B3F1C4 /* LaunchScreen.storyboard */; }; 17 | 0C4B4EBB1D264AEE00B3F1C4 /* UILabel_YBAttributeTapAction_DemoTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 0C4B4EBA1D264AEE00B3F1C4 /* UILabel_YBAttributeTapAction_DemoTests.m */; }; 18 | 0C4B4EC61D264AEE00B3F1C4 /* UILabel_YBAttributeTapAction_DemoUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 0C4B4EC51D264AEE00B3F1C4 /* UILabel_YBAttributeTapAction_DemoUITests.m */; }; 19 | 0CADB3A521A5403C00A54817 /* HomeTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CADB3A321A5403C00A54817 /* HomeTableViewController.m */; }; 20 | 0CADB3A621A5403C00A54817 /* HomeTableViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0CADB3A421A5403C00A54817 /* HomeTableViewController.xib */; }; 21 | 0CADB3AF21A639FC00A54817 /* HomeCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CADB3AD21A639FC00A54817 /* HomeCell.m */; }; 22 | 0CADB3B021A639FC00A54817 /* HomeCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0CADB3AE21A639FC00A54817 /* HomeCell.xib */; }; 23 | 0CADB3B621A6469B00A54817 /* test_1.txt in Resources */ = {isa = PBXBuildFile; fileRef = 0CADB3B521A6469B00A54817 /* test_1.txt */; }; 24 | /* End PBXBuildFile section */ 25 | 26 | /* Begin PBXContainerItemProxy section */ 27 | 0C4B4EB71D264AEE00B3F1C4 /* PBXContainerItemProxy */ = { 28 | isa = PBXContainerItemProxy; 29 | containerPortal = 0C4B4E951D264AEE00B3F1C4 /* Project object */; 30 | proxyType = 1; 31 | remoteGlobalIDString = 0C4B4E9C1D264AEE00B3F1C4; 32 | remoteInfo = "UILabel+YBAttributeTapAction-Demo"; 33 | }; 34 | 0C4B4EC21D264AEE00B3F1C4 /* PBXContainerItemProxy */ = { 35 | isa = PBXContainerItemProxy; 36 | containerPortal = 0C4B4E951D264AEE00B3F1C4 /* Project object */; 37 | proxyType = 1; 38 | remoteGlobalIDString = 0C4B4E9C1D264AEE00B3F1C4; 39 | remoteInfo = "UILabel+YBAttributeTapAction-Demo"; 40 | }; 41 | /* End PBXContainerItemProxy section */ 42 | 43 | /* Begin PBXFileReference section */ 44 | 0C3F7F251D65542B00FD1352 /* UILabel+YBAttributeTextTapAction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UILabel+YBAttributeTextTapAction.h"; sourceTree = ""; }; 45 | 0C3F7F281D65B59000FD1352 /* UILabel+YBAttributeTextTapAction.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UILabel+YBAttributeTextTapAction.m"; sourceTree = ""; }; 46 | 0C4B4E9D1D264AEE00B3F1C4 /* UILabel+YBAttributeTapAction-Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "UILabel+YBAttributeTapAction-Demo.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 47 | 0C4B4EA11D264AEE00B3F1C4 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 48 | 0C4B4EA31D264AEE00B3F1C4 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 49 | 0C4B4EA41D264AEE00B3F1C4 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 50 | 0C4B4EA61D264AEE00B3F1C4 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 51 | 0C4B4EA71D264AEE00B3F1C4 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 52 | 0C4B4EAA1D264AEE00B3F1C4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 53 | 0C4B4EAC1D264AEE00B3F1C4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 54 | 0C4B4EAF1D264AEE00B3F1C4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 55 | 0C4B4EB11D264AEE00B3F1C4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 56 | 0C4B4EB61D264AEE00B3F1C4 /* UILabel+YBAttributeTapAction-DemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "UILabel+YBAttributeTapAction-DemoTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 57 | 0C4B4EBA1D264AEE00B3F1C4 /* UILabel_YBAttributeTapAction_DemoTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = UILabel_YBAttributeTapAction_DemoTests.m; sourceTree = ""; }; 58 | 0C4B4EBC1D264AEE00B3F1C4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 59 | 0C4B4EC11D264AEE00B3F1C4 /* UILabel+YBAttributeTapAction-DemoUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "UILabel+YBAttributeTapAction-DemoUITests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 60 | 0C4B4EC51D264AEE00B3F1C4 /* UILabel_YBAttributeTapAction_DemoUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = UILabel_YBAttributeTapAction_DemoUITests.m; sourceTree = ""; }; 61 | 0C4B4EC71D264AEE00B3F1C4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 62 | 0CADB3A221A5403C00A54817 /* HomeTableViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HomeTableViewController.h; sourceTree = ""; }; 63 | 0CADB3A321A5403C00A54817 /* HomeTableViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HomeTableViewController.m; sourceTree = ""; }; 64 | 0CADB3A421A5403C00A54817 /* HomeTableViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = HomeTableViewController.xib; sourceTree = ""; }; 65 | 0CADB3AC21A639FC00A54817 /* HomeCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HomeCell.h; sourceTree = ""; }; 66 | 0CADB3AD21A639FC00A54817 /* HomeCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HomeCell.m; sourceTree = ""; }; 67 | 0CADB3AE21A639FC00A54817 /* HomeCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = HomeCell.xib; sourceTree = ""; }; 68 | 0CADB3B521A6469B00A54817 /* test_1.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = test_1.txt; sourceTree = ""; }; 69 | /* End PBXFileReference section */ 70 | 71 | /* Begin PBXFrameworksBuildPhase section */ 72 | 0C4B4E9A1D264AEE00B3F1C4 /* Frameworks */ = { 73 | isa = PBXFrameworksBuildPhase; 74 | buildActionMask = 2147483647; 75 | files = ( 76 | ); 77 | runOnlyForDeploymentPostprocessing = 0; 78 | }; 79 | 0C4B4EB31D264AEE00B3F1C4 /* Frameworks */ = { 80 | isa = PBXFrameworksBuildPhase; 81 | buildActionMask = 2147483647; 82 | files = ( 83 | ); 84 | runOnlyForDeploymentPostprocessing = 0; 85 | }; 86 | 0C4B4EBE1D264AEE00B3F1C4 /* Frameworks */ = { 87 | isa = PBXFrameworksBuildPhase; 88 | buildActionMask = 2147483647; 89 | files = ( 90 | ); 91 | runOnlyForDeploymentPostprocessing = 0; 92 | }; 93 | /* End PBXFrameworksBuildPhase section */ 94 | 95 | /* Begin PBXGroup section */ 96 | 0C3F7F241D65542B00FD1352 /* YBAttributeTextTapAction */ = { 97 | isa = PBXGroup; 98 | children = ( 99 | 0C3F7F251D65542B00FD1352 /* UILabel+YBAttributeTextTapAction.h */, 100 | 0C3F7F281D65B59000FD1352 /* UILabel+YBAttributeTextTapAction.m */, 101 | ); 102 | path = YBAttributeTextTapAction; 103 | sourceTree = ""; 104 | }; 105 | 0C4B4E941D264AEE00B3F1C4 = { 106 | isa = PBXGroup; 107 | children = ( 108 | 0C4B4E9F1D264AEE00B3F1C4 /* UILabel+YBAttributeTapAction-Demo */, 109 | 0C4B4EB91D264AEE00B3F1C4 /* UILabel+YBAttributeTapAction-DemoTests */, 110 | 0C4B4EC41D264AEE00B3F1C4 /* UILabel+YBAttributeTapAction-DemoUITests */, 111 | 0C4B4E9E1D264AEE00B3F1C4 /* Products */, 112 | ); 113 | sourceTree = ""; 114 | }; 115 | 0C4B4E9E1D264AEE00B3F1C4 /* Products */ = { 116 | isa = PBXGroup; 117 | children = ( 118 | 0C4B4E9D1D264AEE00B3F1C4 /* UILabel+YBAttributeTapAction-Demo.app */, 119 | 0C4B4EB61D264AEE00B3F1C4 /* UILabel+YBAttributeTapAction-DemoTests.xctest */, 120 | 0C4B4EC11D264AEE00B3F1C4 /* UILabel+YBAttributeTapAction-DemoUITests.xctest */, 121 | ); 122 | name = Products; 123 | sourceTree = ""; 124 | }; 125 | 0C4B4E9F1D264AEE00B3F1C4 /* UILabel+YBAttributeTapAction-Demo */ = { 126 | isa = PBXGroup; 127 | children = ( 128 | 0C3F7F241D65542B00FD1352 /* YBAttributeTextTapAction */, 129 | 0CADB3B421A6468500A54817 /* Resources */, 130 | 0CADB3B321A644C600A54817 /* Home */, 131 | 0CADB3B221A644A600A54817 /* Other */, 132 | 0C4B4EAC1D264AEE00B3F1C4 /* Assets.xcassets */, 133 | 0C4B4EB11D264AEE00B3F1C4 /* Info.plist */, 134 | 0C4B4EA01D264AEE00B3F1C4 /* Supporting Files */, 135 | ); 136 | path = "UILabel+YBAttributeTapAction-Demo"; 137 | sourceTree = ""; 138 | }; 139 | 0C4B4EA01D264AEE00B3F1C4 /* Supporting Files */ = { 140 | isa = PBXGroup; 141 | children = ( 142 | 0C4B4EA11D264AEE00B3F1C4 /* main.m */, 143 | ); 144 | name = "Supporting Files"; 145 | sourceTree = ""; 146 | }; 147 | 0C4B4EB91D264AEE00B3F1C4 /* UILabel+YBAttributeTapAction-DemoTests */ = { 148 | isa = PBXGroup; 149 | children = ( 150 | 0C4B4EBA1D264AEE00B3F1C4 /* UILabel_YBAttributeTapAction_DemoTests.m */, 151 | 0C4B4EBC1D264AEE00B3F1C4 /* Info.plist */, 152 | ); 153 | path = "UILabel+YBAttributeTapAction-DemoTests"; 154 | sourceTree = ""; 155 | }; 156 | 0C4B4EC41D264AEE00B3F1C4 /* UILabel+YBAttributeTapAction-DemoUITests */ = { 157 | isa = PBXGroup; 158 | children = ( 159 | 0C4B4EC51D264AEE00B3F1C4 /* UILabel_YBAttributeTapAction_DemoUITests.m */, 160 | 0C4B4EC71D264AEE00B3F1C4 /* Info.plist */, 161 | ); 162 | path = "UILabel+YBAttributeTapAction-DemoUITests"; 163 | sourceTree = ""; 164 | }; 165 | 0CADB3B221A644A600A54817 /* Other */ = { 166 | isa = PBXGroup; 167 | children = ( 168 | 0C4B4EA31D264AEE00B3F1C4 /* AppDelegate.h */, 169 | 0C4B4EA41D264AEE00B3F1C4 /* AppDelegate.m */, 170 | 0C4B4EA91D264AEE00B3F1C4 /* Main.storyboard */, 171 | 0C4B4EAE1D264AEE00B3F1C4 /* LaunchScreen.storyboard */, 172 | 0C4B4EA61D264AEE00B3F1C4 /* ViewController.h */, 173 | 0C4B4EA71D264AEE00B3F1C4 /* ViewController.m */, 174 | ); 175 | path = Other; 176 | sourceTree = ""; 177 | }; 178 | 0CADB3B321A644C600A54817 /* Home */ = { 179 | isa = PBXGroup; 180 | children = ( 181 | 0CADB3A221A5403C00A54817 /* HomeTableViewController.h */, 182 | 0CADB3A321A5403C00A54817 /* HomeTableViewController.m */, 183 | 0CADB3A421A5403C00A54817 /* HomeTableViewController.xib */, 184 | 0CADB3AC21A639FC00A54817 /* HomeCell.h */, 185 | 0CADB3AD21A639FC00A54817 /* HomeCell.m */, 186 | 0CADB3AE21A639FC00A54817 /* HomeCell.xib */, 187 | ); 188 | path = Home; 189 | sourceTree = ""; 190 | }; 191 | 0CADB3B421A6468500A54817 /* Resources */ = { 192 | isa = PBXGroup; 193 | children = ( 194 | 0CADB3B521A6469B00A54817 /* test_1.txt */, 195 | ); 196 | path = Resources; 197 | sourceTree = ""; 198 | }; 199 | /* End PBXGroup section */ 200 | 201 | /* Begin PBXNativeTarget section */ 202 | 0C4B4E9C1D264AEE00B3F1C4 /* UILabel+YBAttributeTapAction-Demo */ = { 203 | isa = PBXNativeTarget; 204 | buildConfigurationList = 0C4B4ECA1D264AEE00B3F1C4 /* Build configuration list for PBXNativeTarget "UILabel+YBAttributeTapAction-Demo" */; 205 | buildPhases = ( 206 | 0C4B4E991D264AEE00B3F1C4 /* Sources */, 207 | 0C4B4E9A1D264AEE00B3F1C4 /* Frameworks */, 208 | 0C4B4E9B1D264AEE00B3F1C4 /* Resources */, 209 | ); 210 | buildRules = ( 211 | ); 212 | dependencies = ( 213 | ); 214 | name = "UILabel+YBAttributeTapAction-Demo"; 215 | productName = "UILabel+YBAttributeTapAction-Demo"; 216 | productReference = 0C4B4E9D1D264AEE00B3F1C4 /* UILabel+YBAttributeTapAction-Demo.app */; 217 | productType = "com.apple.product-type.application"; 218 | }; 219 | 0C4B4EB51D264AEE00B3F1C4 /* UILabel+YBAttributeTapAction-DemoTests */ = { 220 | isa = PBXNativeTarget; 221 | buildConfigurationList = 0C4B4ECD1D264AEE00B3F1C4 /* Build configuration list for PBXNativeTarget "UILabel+YBAttributeTapAction-DemoTests" */; 222 | buildPhases = ( 223 | 0C4B4EB21D264AEE00B3F1C4 /* Sources */, 224 | 0C4B4EB31D264AEE00B3F1C4 /* Frameworks */, 225 | 0C4B4EB41D264AEE00B3F1C4 /* Resources */, 226 | ); 227 | buildRules = ( 228 | ); 229 | dependencies = ( 230 | 0C4B4EB81D264AEE00B3F1C4 /* PBXTargetDependency */, 231 | ); 232 | name = "UILabel+YBAttributeTapAction-DemoTests"; 233 | productName = "UILabel+YBAttributeTapAction-DemoTests"; 234 | productReference = 0C4B4EB61D264AEE00B3F1C4 /* UILabel+YBAttributeTapAction-DemoTests.xctest */; 235 | productType = "com.apple.product-type.bundle.unit-test"; 236 | }; 237 | 0C4B4EC01D264AEE00B3F1C4 /* UILabel+YBAttributeTapAction-DemoUITests */ = { 238 | isa = PBXNativeTarget; 239 | buildConfigurationList = 0C4B4ED01D264AEE00B3F1C4 /* Build configuration list for PBXNativeTarget "UILabel+YBAttributeTapAction-DemoUITests" */; 240 | buildPhases = ( 241 | 0C4B4EBD1D264AEE00B3F1C4 /* Sources */, 242 | 0C4B4EBE1D264AEE00B3F1C4 /* Frameworks */, 243 | 0C4B4EBF1D264AEE00B3F1C4 /* Resources */, 244 | ); 245 | buildRules = ( 246 | ); 247 | dependencies = ( 248 | 0C4B4EC31D264AEE00B3F1C4 /* PBXTargetDependency */, 249 | ); 250 | name = "UILabel+YBAttributeTapAction-DemoUITests"; 251 | productName = "UILabel+YBAttributeTapAction-DemoUITests"; 252 | productReference = 0C4B4EC11D264AEE00B3F1C4 /* UILabel+YBAttributeTapAction-DemoUITests.xctest */; 253 | productType = "com.apple.product-type.bundle.ui-testing"; 254 | }; 255 | /* End PBXNativeTarget section */ 256 | 257 | /* Begin PBXProject section */ 258 | 0C4B4E951D264AEE00B3F1C4 /* Project object */ = { 259 | isa = PBXProject; 260 | attributes = { 261 | LastUpgradeCheck = 0730; 262 | ORGANIZATIONNAME = LYB; 263 | TargetAttributes = { 264 | 0C4B4E9C1D264AEE00B3F1C4 = { 265 | CreatedOnToolsVersion = 7.3; 266 | DevelopmentTeam = 4URW8PS6WP; 267 | ProvisioningStyle = Manual; 268 | }; 269 | 0C4B4EB51D264AEE00B3F1C4 = { 270 | CreatedOnToolsVersion = 7.3; 271 | TestTargetID = 0C4B4E9C1D264AEE00B3F1C4; 272 | }; 273 | 0C4B4EC01D264AEE00B3F1C4 = { 274 | CreatedOnToolsVersion = 7.3; 275 | TestTargetID = 0C4B4E9C1D264AEE00B3F1C4; 276 | }; 277 | }; 278 | }; 279 | buildConfigurationList = 0C4B4E981D264AEE00B3F1C4 /* Build configuration list for PBXProject "UILabel+YBAttributeTapAction-Demo" */; 280 | compatibilityVersion = "Xcode 3.2"; 281 | developmentRegion = English; 282 | hasScannedForEncodings = 0; 283 | knownRegions = ( 284 | en, 285 | Base, 286 | ); 287 | mainGroup = 0C4B4E941D264AEE00B3F1C4; 288 | productRefGroup = 0C4B4E9E1D264AEE00B3F1C4 /* Products */; 289 | projectDirPath = ""; 290 | projectRoot = ""; 291 | targets = ( 292 | 0C4B4E9C1D264AEE00B3F1C4 /* UILabel+YBAttributeTapAction-Demo */, 293 | 0C4B4EB51D264AEE00B3F1C4 /* UILabel+YBAttributeTapAction-DemoTests */, 294 | 0C4B4EC01D264AEE00B3F1C4 /* UILabel+YBAttributeTapAction-DemoUITests */, 295 | ); 296 | }; 297 | /* End PBXProject section */ 298 | 299 | /* Begin PBXResourcesBuildPhase section */ 300 | 0C4B4E9B1D264AEE00B3F1C4 /* Resources */ = { 301 | isa = PBXResourcesBuildPhase; 302 | buildActionMask = 2147483647; 303 | files = ( 304 | 0CADB3A621A5403C00A54817 /* HomeTableViewController.xib in Resources */, 305 | 0C4B4EB01D264AEE00B3F1C4 /* LaunchScreen.storyboard in Resources */, 306 | 0CADB3B621A6469B00A54817 /* test_1.txt in Resources */, 307 | 0C4B4EAD1D264AEE00B3F1C4 /* Assets.xcassets in Resources */, 308 | 0C4B4EAB1D264AEE00B3F1C4 /* Main.storyboard in Resources */, 309 | 0CADB3B021A639FC00A54817 /* HomeCell.xib in Resources */, 310 | ); 311 | runOnlyForDeploymentPostprocessing = 0; 312 | }; 313 | 0C4B4EB41D264AEE00B3F1C4 /* Resources */ = { 314 | isa = PBXResourcesBuildPhase; 315 | buildActionMask = 2147483647; 316 | files = ( 317 | ); 318 | runOnlyForDeploymentPostprocessing = 0; 319 | }; 320 | 0C4B4EBF1D264AEE00B3F1C4 /* Resources */ = { 321 | isa = PBXResourcesBuildPhase; 322 | buildActionMask = 2147483647; 323 | files = ( 324 | ); 325 | runOnlyForDeploymentPostprocessing = 0; 326 | }; 327 | /* End PBXResourcesBuildPhase section */ 328 | 329 | /* Begin PBXSourcesBuildPhase section */ 330 | 0C4B4E991D264AEE00B3F1C4 /* Sources */ = { 331 | isa = PBXSourcesBuildPhase; 332 | buildActionMask = 2147483647; 333 | files = ( 334 | 0CADB3AF21A639FC00A54817 /* HomeCell.m in Sources */, 335 | 0C4B4EA81D264AEE00B3F1C4 /* ViewController.m in Sources */, 336 | 0CADB3A521A5403C00A54817 /* HomeTableViewController.m in Sources */, 337 | 0C3F7F291D65B59000FD1352 /* UILabel+YBAttributeTextTapAction.m in Sources */, 338 | 0C4B4EA51D264AEE00B3F1C4 /* AppDelegate.m in Sources */, 339 | 0C4B4EA21D264AEE00B3F1C4 /* main.m in Sources */, 340 | ); 341 | runOnlyForDeploymentPostprocessing = 0; 342 | }; 343 | 0C4B4EB21D264AEE00B3F1C4 /* Sources */ = { 344 | isa = PBXSourcesBuildPhase; 345 | buildActionMask = 2147483647; 346 | files = ( 347 | 0C4B4EBB1D264AEE00B3F1C4 /* UILabel_YBAttributeTapAction_DemoTests.m in Sources */, 348 | ); 349 | runOnlyForDeploymentPostprocessing = 0; 350 | }; 351 | 0C4B4EBD1D264AEE00B3F1C4 /* Sources */ = { 352 | isa = PBXSourcesBuildPhase; 353 | buildActionMask = 2147483647; 354 | files = ( 355 | 0C4B4EC61D264AEE00B3F1C4 /* UILabel_YBAttributeTapAction_DemoUITests.m in Sources */, 356 | ); 357 | runOnlyForDeploymentPostprocessing = 0; 358 | }; 359 | /* End PBXSourcesBuildPhase section */ 360 | 361 | /* Begin PBXTargetDependency section */ 362 | 0C4B4EB81D264AEE00B3F1C4 /* PBXTargetDependency */ = { 363 | isa = PBXTargetDependency; 364 | target = 0C4B4E9C1D264AEE00B3F1C4 /* UILabel+YBAttributeTapAction-Demo */; 365 | targetProxy = 0C4B4EB71D264AEE00B3F1C4 /* PBXContainerItemProxy */; 366 | }; 367 | 0C4B4EC31D264AEE00B3F1C4 /* PBXTargetDependency */ = { 368 | isa = PBXTargetDependency; 369 | target = 0C4B4E9C1D264AEE00B3F1C4 /* UILabel+YBAttributeTapAction-Demo */; 370 | targetProxy = 0C4B4EC21D264AEE00B3F1C4 /* PBXContainerItemProxy */; 371 | }; 372 | /* End PBXTargetDependency section */ 373 | 374 | /* Begin PBXVariantGroup section */ 375 | 0C4B4EA91D264AEE00B3F1C4 /* Main.storyboard */ = { 376 | isa = PBXVariantGroup; 377 | children = ( 378 | 0C4B4EAA1D264AEE00B3F1C4 /* Base */, 379 | ); 380 | name = Main.storyboard; 381 | sourceTree = ""; 382 | }; 383 | 0C4B4EAE1D264AEE00B3F1C4 /* LaunchScreen.storyboard */ = { 384 | isa = PBXVariantGroup; 385 | children = ( 386 | 0C4B4EAF1D264AEE00B3F1C4 /* Base */, 387 | ); 388 | name = LaunchScreen.storyboard; 389 | sourceTree = ""; 390 | }; 391 | /* End PBXVariantGroup section */ 392 | 393 | /* Begin XCBuildConfiguration section */ 394 | 0C4B4EC81D264AEE00B3F1C4 /* Debug */ = { 395 | isa = XCBuildConfiguration; 396 | buildSettings = { 397 | ALWAYS_SEARCH_USER_PATHS = NO; 398 | CLANG_ANALYZER_NONNULL = YES; 399 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 400 | CLANG_CXX_LIBRARY = "libc++"; 401 | CLANG_ENABLE_MODULES = YES; 402 | CLANG_ENABLE_OBJC_ARC = YES; 403 | CLANG_WARN_BOOL_CONVERSION = YES; 404 | CLANG_WARN_CONSTANT_CONVERSION = YES; 405 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 406 | CLANG_WARN_EMPTY_BODY = YES; 407 | CLANG_WARN_ENUM_CONVERSION = YES; 408 | CLANG_WARN_INT_CONVERSION = YES; 409 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 410 | CLANG_WARN_UNREACHABLE_CODE = YES; 411 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 412 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 413 | COPY_PHASE_STRIP = NO; 414 | DEBUG_INFORMATION_FORMAT = dwarf; 415 | ENABLE_STRICT_OBJC_MSGSEND = YES; 416 | ENABLE_TESTABILITY = YES; 417 | GCC_C_LANGUAGE_STANDARD = gnu99; 418 | GCC_DYNAMIC_NO_PIC = NO; 419 | GCC_NO_COMMON_BLOCKS = YES; 420 | GCC_OPTIMIZATION_LEVEL = 0; 421 | GCC_PREPROCESSOR_DEFINITIONS = ( 422 | "DEBUG=1", 423 | "$(inherited)", 424 | ); 425 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 426 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 427 | GCC_WARN_UNDECLARED_SELECTOR = YES; 428 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 429 | GCC_WARN_UNUSED_FUNCTION = YES; 430 | GCC_WARN_UNUSED_VARIABLE = YES; 431 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 432 | MTL_ENABLE_DEBUG_INFO = YES; 433 | ONLY_ACTIVE_ARCH = YES; 434 | SDKROOT = iphoneos; 435 | }; 436 | name = Debug; 437 | }; 438 | 0C4B4EC91D264AEE00B3F1C4 /* Release */ = { 439 | isa = XCBuildConfiguration; 440 | buildSettings = { 441 | ALWAYS_SEARCH_USER_PATHS = NO; 442 | CLANG_ANALYZER_NONNULL = YES; 443 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 444 | CLANG_CXX_LIBRARY = "libc++"; 445 | CLANG_ENABLE_MODULES = YES; 446 | CLANG_ENABLE_OBJC_ARC = YES; 447 | CLANG_WARN_BOOL_CONVERSION = YES; 448 | CLANG_WARN_CONSTANT_CONVERSION = YES; 449 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 450 | CLANG_WARN_EMPTY_BODY = YES; 451 | CLANG_WARN_ENUM_CONVERSION = YES; 452 | CLANG_WARN_INT_CONVERSION = YES; 453 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 454 | CLANG_WARN_UNREACHABLE_CODE = YES; 455 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 456 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 457 | COPY_PHASE_STRIP = NO; 458 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 459 | ENABLE_NS_ASSERTIONS = NO; 460 | ENABLE_STRICT_OBJC_MSGSEND = YES; 461 | GCC_C_LANGUAGE_STANDARD = gnu99; 462 | GCC_NO_COMMON_BLOCKS = YES; 463 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 464 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 465 | GCC_WARN_UNDECLARED_SELECTOR = YES; 466 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 467 | GCC_WARN_UNUSED_FUNCTION = YES; 468 | GCC_WARN_UNUSED_VARIABLE = YES; 469 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 470 | MTL_ENABLE_DEBUG_INFO = NO; 471 | SDKROOT = iphoneos; 472 | VALIDATE_PRODUCT = YES; 473 | }; 474 | name = Release; 475 | }; 476 | 0C4B4ECB1D264AEE00B3F1C4 /* Debug */ = { 477 | isa = XCBuildConfiguration; 478 | buildSettings = { 479 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 480 | CODE_SIGN_IDENTITY = "iPhone Distribution"; 481 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; 482 | CODE_SIGN_STYLE = Manual; 483 | DEVELOPMENT_TEAM = 4URW8PS6WP; 484 | INFOPLIST_FILE = "UILabel+YBAttributeTapAction-Demo/Info.plist"; 485 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 486 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 487 | PRODUCT_BUNDLE_IDENTIFIER = "com.chinaums.YBAttributeTextTapAction-Demo"; 488 | PRODUCT_NAME = "$(TARGET_NAME)"; 489 | PROVISIONING_PROFILE = ""; 490 | PROVISIONING_PROFILE_SPECIFIER = DevelopInHouse; 491 | }; 492 | name = Debug; 493 | }; 494 | 0C4B4ECC1D264AEE00B3F1C4 /* Release */ = { 495 | isa = XCBuildConfiguration; 496 | buildSettings = { 497 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 498 | CODE_SIGN_IDENTITY = "iPhone Distribution"; 499 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; 500 | CODE_SIGN_STYLE = Manual; 501 | DEVELOPMENT_TEAM = 4URW8PS6WP; 502 | INFOPLIST_FILE = "UILabel+YBAttributeTapAction-Demo/Info.plist"; 503 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 504 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 505 | PRODUCT_BUNDLE_IDENTIFIER = "com.chinaums.YBAttributeTextTapAction-Demo"; 506 | PRODUCT_NAME = "$(TARGET_NAME)"; 507 | PROVISIONING_PROFILE = ""; 508 | PROVISIONING_PROFILE_SPECIFIER = DevelopInHouse; 509 | }; 510 | name = Release; 511 | }; 512 | 0C4B4ECE1D264AEE00B3F1C4 /* Debug */ = { 513 | isa = XCBuildConfiguration; 514 | buildSettings = { 515 | BUNDLE_LOADER = "$(TEST_HOST)"; 516 | INFOPLIST_FILE = "UILabel+YBAttributeTapAction-DemoTests/Info.plist"; 517 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 518 | PRODUCT_BUNDLE_IDENTIFIER = "Lee.UILabel-YBAttributeTapAction-DemoTests"; 519 | PRODUCT_NAME = "$(TARGET_NAME)"; 520 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/UILabel+YBAttributeTapAction-Demo.app/UILabel+YBAttributeTapAction-Demo"; 521 | }; 522 | name = Debug; 523 | }; 524 | 0C4B4ECF1D264AEE00B3F1C4 /* Release */ = { 525 | isa = XCBuildConfiguration; 526 | buildSettings = { 527 | BUNDLE_LOADER = "$(TEST_HOST)"; 528 | INFOPLIST_FILE = "UILabel+YBAttributeTapAction-DemoTests/Info.plist"; 529 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 530 | PRODUCT_BUNDLE_IDENTIFIER = "Lee.UILabel-YBAttributeTapAction-DemoTests"; 531 | PRODUCT_NAME = "$(TARGET_NAME)"; 532 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/UILabel+YBAttributeTapAction-Demo.app/UILabel+YBAttributeTapAction-Demo"; 533 | }; 534 | name = Release; 535 | }; 536 | 0C4B4ED11D264AEE00B3F1C4 /* Debug */ = { 537 | isa = XCBuildConfiguration; 538 | buildSettings = { 539 | INFOPLIST_FILE = "UILabel+YBAttributeTapAction-DemoUITests/Info.plist"; 540 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 541 | PRODUCT_BUNDLE_IDENTIFIER = "Lee.UILabel-YBAttributeTapAction-DemoUITests"; 542 | PRODUCT_NAME = "$(TARGET_NAME)"; 543 | TEST_TARGET_NAME = "UILabel+YBAttributeTapAction-Demo"; 544 | }; 545 | name = Debug; 546 | }; 547 | 0C4B4ED21D264AEE00B3F1C4 /* Release */ = { 548 | isa = XCBuildConfiguration; 549 | buildSettings = { 550 | INFOPLIST_FILE = "UILabel+YBAttributeTapAction-DemoUITests/Info.plist"; 551 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 552 | PRODUCT_BUNDLE_IDENTIFIER = "Lee.UILabel-YBAttributeTapAction-DemoUITests"; 553 | PRODUCT_NAME = "$(TARGET_NAME)"; 554 | TEST_TARGET_NAME = "UILabel+YBAttributeTapAction-Demo"; 555 | }; 556 | name = Release; 557 | }; 558 | /* End XCBuildConfiguration section */ 559 | 560 | /* Begin XCConfigurationList section */ 561 | 0C4B4E981D264AEE00B3F1C4 /* Build configuration list for PBXProject "UILabel+YBAttributeTapAction-Demo" */ = { 562 | isa = XCConfigurationList; 563 | buildConfigurations = ( 564 | 0C4B4EC81D264AEE00B3F1C4 /* Debug */, 565 | 0C4B4EC91D264AEE00B3F1C4 /* Release */, 566 | ); 567 | defaultConfigurationIsVisible = 0; 568 | defaultConfigurationName = Release; 569 | }; 570 | 0C4B4ECA1D264AEE00B3F1C4 /* Build configuration list for PBXNativeTarget "UILabel+YBAttributeTapAction-Demo" */ = { 571 | isa = XCConfigurationList; 572 | buildConfigurations = ( 573 | 0C4B4ECB1D264AEE00B3F1C4 /* Debug */, 574 | 0C4B4ECC1D264AEE00B3F1C4 /* Release */, 575 | ); 576 | defaultConfigurationIsVisible = 0; 577 | defaultConfigurationName = Release; 578 | }; 579 | 0C4B4ECD1D264AEE00B3F1C4 /* Build configuration list for PBXNativeTarget "UILabel+YBAttributeTapAction-DemoTests" */ = { 580 | isa = XCConfigurationList; 581 | buildConfigurations = ( 582 | 0C4B4ECE1D264AEE00B3F1C4 /* Debug */, 583 | 0C4B4ECF1D264AEE00B3F1C4 /* Release */, 584 | ); 585 | defaultConfigurationIsVisible = 0; 586 | defaultConfigurationName = Release; 587 | }; 588 | 0C4B4ED01D264AEE00B3F1C4 /* Build configuration list for PBXNativeTarget "UILabel+YBAttributeTapAction-DemoUITests" */ = { 589 | isa = XCConfigurationList; 590 | buildConfigurations = ( 591 | 0C4B4ED11D264AEE00B3F1C4 /* Debug */, 592 | 0C4B4ED21D264AEE00B3F1C4 /* Release */, 593 | ); 594 | defaultConfigurationIsVisible = 0; 595 | defaultConfigurationName = Release; 596 | }; 597 | /* End XCConfigurationList section */ 598 | }; 599 | rootObject = 0C4B4E951D264AEE00B3F1C4 /* Project object */; 600 | } 601 | -------------------------------------------------------------------------------- /UILabel+YBAttributeTapAction-Demo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /UILabel+YBAttributeTapAction-Demo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /UILabel+YBAttributeTapAction-Demo.xcodeproj/project.xcworkspace/xcuserdata/LYB.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lyb5834/YBAttributeTextTapAction/dbb3b856cb9ed9cedbcd70df09537f655638728f/UILabel+YBAttributeTapAction-Demo.xcodeproj/project.xcworkspace/xcuserdata/LYB.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /UILabel+YBAttributeTapAction-Demo.xcodeproj/xcuserdata/LYB.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /UILabel+YBAttributeTapAction-Demo.xcodeproj/xcuserdata/LYB.xcuserdatad/xcschemes/UILabel+YBAttributeTapAction-Demo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 43 | 49 | 50 | 51 | 52 | 53 | 59 | 60 | 61 | 62 | 63 | 64 | 74 | 76 | 82 | 83 | 84 | 85 | 86 | 87 | 93 | 95 | 101 | 102 | 103 | 104 | 106 | 107 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /UILabel+YBAttributeTapAction-Demo.xcodeproj/xcuserdata/LYB.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | UILabel+YBAttributeTapAction-Demo.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 0C4B4E9C1D264AEE00B3F1C4 16 | 17 | primary 18 | 19 | 20 | 0C4B4EB51D264AEE00B3F1C4 21 | 22 | primary 23 | 24 | 25 | 0C4B4EC01D264AEE00B3F1C4 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /UILabel+YBAttributeTapAction-Demo/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 | } -------------------------------------------------------------------------------- /UILabel+YBAttributeTapAction-Demo/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /UILabel+YBAttributeTapAction-Demo/Assets.xcassets/btn_sel.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "xgb_3_sel@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "xgb_3_sel@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /UILabel+YBAttributeTapAction-Demo/Assets.xcassets/btn_sel.imageset/xgb_3_sel@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lyb5834/YBAttributeTextTapAction/dbb3b856cb9ed9cedbcd70df09537f655638728f/UILabel+YBAttributeTapAction-Demo/Assets.xcassets/btn_sel.imageset/xgb_3_sel@2x.png -------------------------------------------------------------------------------- /UILabel+YBAttributeTapAction-Demo/Assets.xcassets/btn_sel.imageset/xgb_3_sel@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lyb5834/YBAttributeTextTapAction/dbb3b856cb9ed9cedbcd70df09537f655638728f/UILabel+YBAttributeTapAction-Demo/Assets.xcassets/btn_sel.imageset/xgb_3_sel@3x.png -------------------------------------------------------------------------------- /UILabel+YBAttributeTapAction-Demo/Assets.xcassets/btn_unSel.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "xgb_3_unSel@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "xgb_3_unSel@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /UILabel+YBAttributeTapAction-Demo/Assets.xcassets/btn_unSel.imageset/xgb_3_unSel@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lyb5834/YBAttributeTextTapAction/dbb3b856cb9ed9cedbcd70df09537f655638728f/UILabel+YBAttributeTapAction-Demo/Assets.xcassets/btn_unSel.imageset/xgb_3_unSel@2x.png -------------------------------------------------------------------------------- /UILabel+YBAttributeTapAction-Demo/Assets.xcassets/btn_unSel.imageset/xgb_3_unSel@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lyb5834/YBAttributeTextTapAction/dbb3b856cb9ed9cedbcd70df09537f655638728f/UILabel+YBAttributeTapAction-Demo/Assets.xcassets/btn_unSel.imageset/xgb_3_unSel@3x.png -------------------------------------------------------------------------------- /UILabel+YBAttributeTapAction-Demo/Home/HomeCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // HomeCell.h 3 | // UILabel+YBAttributeTapAction-Demo 4 | // 5 | // Created by lyb on 2018/11/22. 6 | // Copyright © 2018年 LYB. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | static NSString * const HomeCellID = @"HomeCellID"; 14 | @interface HomeCell : UITableViewCell 15 | @property (weak, nonatomic) IBOutlet UILabel *testTapLabel; 16 | 17 | @end 18 | 19 | NS_ASSUME_NONNULL_END 20 | -------------------------------------------------------------------------------- /UILabel+YBAttributeTapAction-Demo/Home/HomeCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // HomeCell.m 3 | // UILabel+YBAttributeTapAction-Demo 4 | // 5 | // Created by lyb on 2018/11/22. 6 | // Copyright © 2018年 LYB. All rights reserved. 7 | // 8 | 9 | #import "HomeCell.h" 10 | 11 | @implementation HomeCell 12 | 13 | - (void)awakeFromNib { 14 | [super awakeFromNib]; 15 | // Initialization code 16 | } 17 | 18 | - (void)setSelected:(BOOL)selected animated:(BOOL)animated { 19 | [super setSelected:selected animated:animated]; 20 | 21 | // Configure the view for the selected state 22 | } 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /UILabel+YBAttributeTapAction-Demo/Home/HomeCell.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /UILabel+YBAttributeTapAction-Demo/Home/HomeTableViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // HomeTableViewController.h 3 | // UILabel+YBAttributeTapAction-Demo 4 | // 5 | // Created by lyb on 2018/11/21. 6 | // Copyright © 2018年 LYB. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface HomeTableViewController : UITableViewController 14 | 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /UILabel+YBAttributeTapAction-Demo/Home/HomeTableViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // HomeTableViewController.m 3 | // UILabel+YBAttributeTapAction-Demo 4 | // 5 | // Created by lyb on 2018/11/21. 6 | // Copyright © 2018年 LYB. All rights reserved. 7 | // 8 | 9 | #import "HomeTableViewController.h" 10 | #import "HomeCell.h" 11 | #import "UILabel+YBAttributeTextTapAction.h" 12 | 13 | #define YBAlertShow(messageText,buttonName) \ 14 | UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"提示" message:(messageText) \ 15 | delegate:nil cancelButtonTitle:(buttonName) otherButtonTitles: nil];\ 16 | [alert show]; 17 | @interface HomeTableViewController () 18 | < 19 | YBAttributeTapActionDelegate 20 | > 21 | @property (nonatomic, strong) NSArray * dataArray; 22 | 23 | @property (nonatomic, assign) BOOL isShowTotal; 24 | 25 | @end 26 | 27 | @implementation HomeTableViewController 28 | 29 | - (void)viewDidLoad { 30 | [super viewDidLoad]; 31 | [self setupUI]; 32 | } 33 | 34 | - (void)setupUI 35 | { 36 | self.tableView.tableFooterView = [UIView new]; 37 | self.tableView.sectionHeaderHeight = 30; 38 | [self.tableView registerNib:[UINib nibWithNibName:@"HomeCell" bundle:nil] forCellReuseIdentifier:HomeCellID]; 39 | self.tableView.estimatedRowHeight = 44; 40 | self.tableView.rowHeight = UITableViewAutomaticDimension; 41 | } 42 | 43 | - (void)dealloc 44 | { 45 | NSLog(@"HomeTableViewController 释放了"); 46 | } 47 | 48 | #pragma mark - lazyloads 49 | - (NSArray *)dataArray 50 | { 51 | if (!_dataArray) { 52 | _dataArray = @[ 53 | @"点击展开/收起", 54 | @"点击的几个字符相同", 55 | @"点击的字符不同", 56 | @"label上加手势", 57 | @"点击的字符字体不一", 58 | @"点击的字符不在一行", 59 | @"连续跳转", 60 | @"没有任何点击效果" 61 | ]; 62 | } 63 | return _dataArray; 64 | } 65 | 66 | #pragma mark - Table view data source 67 | 68 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 69 | return self.dataArray.count; 70 | } 71 | 72 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 73 | return 1; 74 | } 75 | 76 | - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section 77 | { 78 | UIView * backView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.tableView.bounds.size.width, 30)]; 79 | backView.backgroundColor = [UIColor blackColor]; 80 | 81 | UILabel * label = [[UILabel alloc] initWithFrame:CGRectMake(15, 0, self.tableView.bounds.size.width - 30, 30)]; 82 | label.textColor = [UIColor whiteColor]; 83 | label.font = [UIFont systemFontOfSize:15]; 84 | label.text = self.dataArray[section]; 85 | [backView addSubview:label]; 86 | 87 | return backView; 88 | } 89 | 90 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 91 | 92 | HomeCell *cell = [tableView dequeueReusableCellWithIdentifier:HomeCellID forIndexPath:indexPath]; 93 | //框架已经做过处理,点击label会响应cell的点击事件,点击具体的字会响应框架的方法 94 | if (indexPath.section == 0) { 95 | cell.testTapLabel.tag = 100; 96 | NSString * totalText = [NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"test_1" ofType:@"txt"] encoding:NSUTF8StringEncoding error:nil]; 97 | totalText = [totalText substringToIndex:totalText.length - 1];//去掉文本最后的一个/n符 98 | if (self.isShowTotal) { 99 | NSString * showText = [NSString stringWithFormat:@"%@收起",totalText]; 100 | NSMutableAttributedString * attString = [[NSMutableAttributedString alloc] initWithString:showText]; 101 | NSDictionary * attDic = @{ 102 | NSFontAttributeName : [UIFont systemFontOfSize:15], 103 | NSForegroundColorAttributeName : [UIColor darkGrayColor] 104 | }; 105 | [attString setAttributes:attDic range:NSMakeRange(0, showText.length)]; 106 | [attString setAttributes:@{NSForegroundColorAttributeName : [UIColor blueColor]} range:NSMakeRange(showText.length - 2, 2)]; 107 | cell.testTapLabel.attributedText = attString; 108 | 109 | //这里要用delegate,block不好处理,每次都要重新写这个方法,因为attributedText有变动了 110 | [cell.testTapLabel yb_addAttributeTapActionWithRanges:@[NSStringFromRange(NSMakeRange(showText.length - 2, 2))] delegate:self]; 111 | }else { 112 | NSString * showText = [NSString stringWithFormat:@"%@展开",[totalText substringToIndex:80]]; 113 | NSMutableAttributedString * attString = [[NSMutableAttributedString alloc] initWithString:showText]; 114 | NSDictionary * attDic = @{ 115 | NSFontAttributeName : [UIFont systemFontOfSize:15], 116 | NSForegroundColorAttributeName : [UIColor darkGrayColor] 117 | }; 118 | [attString setAttributes:attDic range:NSMakeRange(0, showText.length)]; 119 | [attString setAttributes:@{NSForegroundColorAttributeName : [UIColor blueColor]} range:NSMakeRange(showText.length - 2, 2)]; 120 | cell.testTapLabel.attributedText = attString; 121 | 122 | [cell.testTapLabel yb_addAttributeTapActionWithRanges:@[NSStringFromRange(NSMakeRange(showText.length - 2, 2))] delegate:self]; 123 | } 124 | }else if (indexPath.section == 1) { 125 | NSString * showText = @"如您有任何疑问,请联系lyb5834@126.com,\n\n\n\n如您有任何疑问,请联系lyb5834@126.com,\n\n\n\n\n如您有任何疑问,请联系lyb5834@126.com"; 126 | cell.testTapLabel.attributedText = [self getAttributeWith:@[@"lyb5834@126.com",@"lyb5834@126.com",@"lyb5834@126.com"] string:showText orginFont:15 orginColor:[UIColor darkGrayColor] attributeFont:18 attributeColor:[UIColor blueColor]]; 127 | 128 | [cell.testTapLabel yb_addAttributeTapActionWithStrings:@[@"lyb5834@126.com",@"lyb5834@126.com",@"lyb5834@126.com"] tapClicked:^(UILabel *label, NSString *string, NSRange range, NSInteger index) { 129 | 130 | NSString * message = [NSString stringWithFormat:@"点击了\"%@\"字符\nrange:%@\n在数组中是第%ld个",string,NSStringFromRange(range),index + 1]; 131 | YBAlertShow(message, @"知道了"); 132 | }]; 133 | }else if (indexPath.section == 2) { 134 | NSString * showText = @"你的快递包裹到了,签收人:张三,电话:13987654321,送货员:李四,电话:15888888888,收件地址:火星"; 135 | cell.testTapLabel.attributedText = [self getAttributeWith:@[@"13987654321",@"15888888888"] string:showText orginFont:15 orginColor:[UIColor darkGrayColor] attributeFont:15 attributeColor:[UIColor blueColor]]; 136 | 137 | [cell.testTapLabel yb_addAttributeTapActionWithStrings:@[@"13987654321",@"15888888888"] tapClicked:^(UILabel *label, NSString *string, NSRange range, NSInteger index) { 138 | 139 | NSString * message = [NSString stringWithFormat:@"点击了\"%@\"字符\nrange:%@\n在数组中是第%ld个",string,NSStringFromRange(range),index + 1]; 140 | YBAlertShow(message, @"知道了"); 141 | }]; 142 | }else if (indexPath.section == 3) { 143 | NSString * showText = @"点击红色字体会走代理方法,测试点击,点label会响应手势事件,查看控制台输出";//手势比较特别,会同时响应框架代理和手势方法 144 | cell.testTapLabel.attributedText = [self getAttributeWith:@[@"测试点击"] string:showText orginFont:15 orginColor:[UIColor darkGrayColor] attributeFont:15 attributeColor:[UIColor redColor]]; 145 | cell.testTapLabel.tag = 200; 146 | [cell.testTapLabel yb_addAttributeTapActionWithStrings:@[@"测试点击"] delegate:self]; 147 | 148 | UITapGestureRecognizer * tapGes = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onTestGestureRecognizerAction:)]; 149 | cell.testTapLabel.userInteractionEnabled = YES; 150 | [cell.testTapLabel addGestureRecognizer:tapGes]; 151 | }else if (indexPath.section == 4) { 152 | NSString * showText = @"恭喜您获得了一张¥50优惠券,赶快去钱包看看并下单使用吧~"; 153 | NSMutableAttributedString * attString = [[NSMutableAttributedString alloc] initWithString:showText]; 154 | NSDictionary * attDic = @{ 155 | NSFontAttributeName : [UIFont systemFontOfSize:15], 156 | NSForegroundColorAttributeName : [UIColor darkGrayColor] 157 | }; 158 | [attString setAttributes:attDic range:NSMakeRange(0, showText.length)]; 159 | [attString addAttributes:@{NSFontAttributeName : [UIFont systemFontOfSize:13]} range:NSMakeRange(8, 1)]; 160 | [attString addAttributes:@{NSFontAttributeName : [UIFont systemFontOfSize:22]} range:NSMakeRange(9, 2)]; 161 | [attString addAttributes:@{NSForegroundColorAttributeName : [UIColor redColor]} range:NSMakeRange(8, 3)]; 162 | cell.testTapLabel.attributedText = attString; 163 | 164 | [cell.testTapLabel yb_addAttributeTapActionWithStrings:@[@"¥50"] tapClicked:^(UILabel *label, NSString *string, NSRange range, NSInteger index) { 165 | NSString * message = [NSString stringWithFormat:@"点击了\"%@\"字符\nrange:%@\n在数组中是第%ld个",string,NSStringFromRange(range),index + 1]; 166 | YBAlertShow(message, @"知道了"); 167 | }]; 168 | }else if (indexPath.section == 5) { 169 | NSString * showText = @"本项目在GitHub托管,如对您有帮助,麻烦点个star,地址:https://github.com/lyb5834/YBAttributeTextTapAction,祝你开发愉快!!!"; 170 | cell.testTapLabel.attributedText = [self getAttributeWith:@[@"https://github.com/lyb5834/YBAttributeTextTapAction"] string:showText orginFont:15 orginColor:[UIColor darkGrayColor] attributeFont:15 attributeColor:[UIColor blueColor]]; 171 | [cell.testTapLabel yb_addAttributeTapActionWithStrings:@[@"https://github.com/lyb5834/YBAttributeTextTapAction"] tapClicked:^(UILabel *label, NSString *string, NSRange range, NSInteger index) { 172 | NSString * message = [NSString stringWithFormat:@"点击了\"%@\"字符\nrange:%@\n在数组中是第%ld个",string,NSStringFromRange(range),index + 1]; 173 | YBAlertShow(message, @"知道了"); 174 | }]; 175 | }else if (indexPath.section == 6) { 176 | NSString * showText = @"点击\"点我跳下一页\"进入下一页"; 177 | cell.testTapLabel.attributedText = [self getAttributeWith:@[@"\"点我跳下一页\""] string:showText orginFont:15 orginColor:[UIColor darkGrayColor] attributeFont:15 attributeColor:[UIColor blueColor]]; 178 | cell.testTapLabel.tag = 300; 179 | [cell.testTapLabel yb_addAttributeTapActionWithStrings:@[@"\"点我跳下一页\""] delegate:self]; 180 | }else { 181 | NSString * showText = @"此label没有附加任何点击效果\n调用\"yb_removeAttributeTapActions\"方法是为了防止cell复用后,label上还有点击事件"; 182 | cell.testTapLabel.attributedText = [self getAttributeWith:@"yb_removeAttributeTapActions" string:showText orginFont:15 orginColor:[UIColor darkGrayColor] attributeFont:15 attributeColor:[UIColor redColor]]; 183 | [cell.testTapLabel yb_removeAttributeTapActions]; 184 | } 185 | 186 | return cell; 187 | } 188 | 189 | - (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section 190 | { 191 | if (section != self.dataArray.count - 1) { 192 | return nil; 193 | } 194 | static NSString * identifier = @"footerView"; 195 | UITableViewHeaderFooterView * footerView = [tableView dequeueReusableHeaderFooterViewWithIdentifier:identifier]; 196 | if (!footerView) { 197 | footerView = [[UITableViewHeaderFooterView alloc] initWithReuseIdentifier:identifier]; 198 | __block UIButton * button = [[UIButton alloc] initWithFrame:CGRectMake(15, 10, [UIScreen mainScreen].bounds.size.width - 30 , 60)]; 199 | footerView.contentView.backgroundColor = [UIColor yellowColor]; 200 | [button setImage:[UIImage imageNamed:@"btn_unSel"] forState:UIControlStateNormal]; 201 | [button setImage:[UIImage imageNamed:@"btn_sel"] forState:UIControlStateSelected]; 202 | [button setImageEdgeInsets:UIEdgeInsetsMake(0, 0, 34, 0)]; 203 | [button setTitleEdgeInsets:UIEdgeInsetsMake(0, 5, 0, 0)]; 204 | NSString * showText = @"您已阅读并同意《用户注册协议》《用户使用协议》《用户保密协议》《平台使用守则》《平台免责申明》《网络安全协议》"; 205 | NSAttributedString * showAttString = [self getAttributeWith:@[@"《用户注册协议》",@"《用户使用协议》",@"《用户保密协议》",@"《平台使用守则》",@"《平台免责申明》",@"《网络安全协议》"] string:showText orginFont:15 orginColor:[UIColor darkGrayColor] attributeFont:15 attributeColor:[UIColor blueColor]]; 206 | [button setAttributedTitle:showAttString forState:UIControlStateNormal]; 207 | button.titleLabel.numberOfLines = 0; 208 | [footerView.contentView addSubview:button]; 209 | [button addTarget:self action:@selector(onButtonClickAction:) forControlEvents:UIControlEventTouchUpInside]; 210 | [button.titleLabel yb_addAttributeTapActionWithStrings:@[@"您已阅读并同意",@"《用户注册协议》",@"《用户使用协议》",@"《用户保密协议》",@"《平台使用守则》",@"《平台免责申明》",@"《网络安全协议》"] tapClicked:^(UILabel *label, NSString *string, NSRange range, NSInteger index) { 211 | 212 | if ([string isEqualToString:@"您已阅读并同意"]) { 213 | button.selected = !button.selected; 214 | }else { 215 | NSString * message = [NSString stringWithFormat:@"点击了\"%@\"字符\nrange:%@\n在数组中是第%ld个",string,NSStringFromRange(range),index + 1]; 216 | YBAlertShow(message, @"知道了"); 217 | } 218 | 219 | }]; 220 | } 221 | 222 | return footerView; 223 | } 224 | 225 | - (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section 226 | { 227 | if (section != self.dataArray.count - 1) { 228 | return 0; 229 | } 230 | return 80; 231 | } 232 | 233 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 234 | { 235 | NSLog(@"点击了cell"); 236 | } 237 | 238 | #pragma mark - action 239 | - (void)onButtonClickAction:(UIButton *)sender 240 | { 241 | sender.selected = !sender.selected; 242 | } 243 | 244 | #pragma mark - YBAttributeTapActionDelegate 245 | - (void)yb_tapAttributeInLabel:(UILabel *)label string:(NSString *)string range:(NSRange)range index:(NSInteger)index 246 | { 247 | if (label.tag == 100) { 248 | self.isShowTotal = !self.isShowTotal; 249 | [self.tableView reloadRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:0 inSection:0]] withRowAnimation:UITableViewRowAnimationFade]; 250 | }else if (label.tag == 200) { 251 | NSString * message = [NSString stringWithFormat:@"点击了\"%@\"字符\nrange:%@\n在数组中是第%ld个",string,NSStringFromRange(range),index + 1]; 252 | YBAlertShow(message, @"知道了"); 253 | }else if (label.tag == 300) { 254 | HomeTableViewController * homeVC = [[HomeTableViewController alloc] init]; 255 | [self.navigationController pushViewController:homeVC animated:YES]; 256 | } 257 | } 258 | 259 | #pragma mark - func 260 | - (void)onTestGestureRecognizerAction:(UITapGestureRecognizer *)tapGes 261 | { 262 | NSLog(@"点击了手势事件"); 263 | } 264 | 265 | - (NSAttributedString *)getAttributeWith:(id)sender 266 | string:(NSString *)string 267 | orginFont:(CGFloat)orginFont 268 | orginColor:(UIColor *)orginColor 269 | attributeFont:(CGFloat)attributeFont 270 | attributeColor:(UIColor *)attributeColor 271 | { 272 | __block NSMutableAttributedString *totalStr = [[NSMutableAttributedString alloc] initWithString:string]; 273 | [totalStr addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:orginFont] range:NSMakeRange(0, string.length)]; 274 | [totalStr addAttribute:NSForegroundColorAttributeName value:orginColor range:NSMakeRange(0, string.length)]; 275 | NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init]; 276 | [paragraphStyle setLineSpacing:5.0f]; //设置行间距 277 | [paragraphStyle setLineBreakMode:NSLineBreakByTruncatingTail]; 278 | [paragraphStyle setAlignment:NSTextAlignmentLeft]; 279 | [paragraphStyle setLineBreakMode:NSLineBreakByCharWrapping]; 280 | [totalStr addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange(0, [totalStr length])]; 281 | 282 | if ([sender isKindOfClass:[NSArray class]]) { 283 | 284 | __block NSString *oringinStr = string; 285 | __weak typeof(self) weakSelf = self; 286 | 287 | [sender enumerateObjectsUsingBlock:^(NSString * _Nonnull str, NSUInteger idx, BOOL * _Nonnull stop) { 288 | 289 | NSRange range = [oringinStr rangeOfString:str]; 290 | [totalStr addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:attributeFont] range:range]; 291 | [totalStr addAttribute:NSForegroundColorAttributeName value:attributeColor range:range]; 292 | oringinStr = [oringinStr stringByReplacingCharactersInRange:range withString:[weakSelf getStringWithRange:range]]; 293 | }]; 294 | 295 | }else if ([sender isKindOfClass:[NSString class]]) { 296 | 297 | NSRange range = [string rangeOfString:sender]; 298 | 299 | [totalStr addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:attributeFont] range:range]; 300 | [totalStr addAttribute:NSForegroundColorAttributeName value:attributeColor range:range]; 301 | } 302 | return totalStr; 303 | } 304 | 305 | - (NSString *)getStringWithRange:(NSRange)range 306 | { 307 | NSMutableString *string = [NSMutableString string]; 308 | for (int i = 0; i < range.length ; i++) { 309 | [string appendString:@" "]; 310 | } 311 | return string; 312 | } 313 | 314 | @end 315 | -------------------------------------------------------------------------------- /UILabel+YBAttributeTapAction-Demo/Home/HomeTableViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /UILabel+YBAttributeTapAction-Demo/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 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /UILabel+YBAttributeTapAction-Demo/Other/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // UILabel+YBAttributeTapAction-Demo 4 | // 5 | // Created by LYB on 16/7/1. 6 | // Copyright © 2016年 LYB. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /UILabel+YBAttributeTapAction-Demo/Other/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // UILabel+YBAttributeTapAction-Demo 4 | // 5 | // Created by LYB on 16/7/1. 6 | // Copyright © 2016年 LYB. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | - (void)applicationWillResignActive:(UIApplication *)application { 24 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 25 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 26 | } 27 | 28 | - (void)applicationDidEnterBackground:(UIApplication *)application { 29 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 31 | } 32 | 33 | - (void)applicationWillEnterForeground:(UIApplication *)application { 34 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 35 | } 36 | 37 | - (void)applicationDidBecomeActive:(UIApplication *)application { 38 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 39 | } 40 | 41 | - (void)applicationWillTerminate:(UIApplication *)application { 42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /UILabel+YBAttributeTapAction-Demo/Other/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 | -------------------------------------------------------------------------------- /UILabel+YBAttributeTapAction-Demo/Other/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /UILabel+YBAttributeTapAction-Demo/Other/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // UILabel+YBAttributeTapAction-Demo 4 | // 5 | // Created by LYB on 16/7/1. 6 | // Copyright © 2016年 LYB. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /UILabel+YBAttributeTapAction-Demo/Other/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // UILabel+YBAttributeTapAction-Demo 4 | // 5 | // Created by LYB on 16/7/1. 6 | // Copyright © 2016年 LYB. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | 11 | @interface ViewController () 12 | 13 | @end 14 | 15 | @implementation ViewController 16 | 17 | - (void)viewDidLoad { 18 | [super viewDidLoad]; 19 | 20 | } 21 | 22 | - (void)didReceiveMemoryWarning { 23 | [super didReceiveMemoryWarning]; 24 | // Dispose of any resources that can be recreated. 25 | } 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /UILabel+YBAttributeTapAction-Demo/Resources/test_1.txt: -------------------------------------------------------------------------------- 1 | 秦始皇(前259年农历十二月初三—前210年),嬴姓,赵氏,名政,又名赵正(政)、秦政,或称祖龙,秦庄襄王之子。中国历史上著名的政治家、战略家、改革家,完成华夏大一统的铁腕政治人物,也是中国第一个称皇帝的君主。 2 | 秦始皇是出生于赵国都城邯郸(今邯郸),并在此度过了少年时期。前247年,13岁时即王位。 前238年,22岁时,在故都雍城举行了国君成人加冕仪式,开始“亲理朝政”,除掉吕不韦、嫪毐等人, 重用李斯、尉缭,自前230年至前221年,先后灭韩、赵、魏、楚、燕、齐六国,39岁时完成了统一中国大业,建立起一个以汉族为主体统一的中央集权的强大国家——秦朝,并奠定中国本土的疆域。 3 | -------------------------------------------------------------------------------- /UILabel+YBAttributeTapAction-Demo/YBAttributeTextTapAction/UILabel+YBAttributeTextTapAction.h: -------------------------------------------------------------------------------- 1 | // 2 | // UILabel+YBAttributeTextTapAction.h 3 | // 4 | // Created by LYB on 16/7/1. 5 | // Copyright © 2016年 LYB. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | @protocol YBAttributeTapActionDelegate 11 | @optional 12 | /** 13 | * YBAttributeTapActionDelegate 14 | * 15 | * @param string 点击的字符串 16 | * @param range 点击的字符串range 17 | * @param index 点击的字符在数组中的index 18 | */ 19 | - (void)yb_tapAttributeInLabel:(UILabel *)label 20 | string:(NSString *)string 21 | range:(NSRange)range 22 | index:(NSInteger)index; 23 | @end 24 | 25 | 26 | @interface UILabel (YBAttributeTextTapAction) 27 | 28 | /** 29 | * 是否打开点击效果,默认是打开 30 | */ 31 | @property (nonatomic, assign) BOOL enabledTapEffect; 32 | 33 | /** 34 | * 点击高亮色 默认是[UIColor lightGrayColor] 需打开enabledTapEffect才有效 35 | */ 36 | @property (nonatomic, strong) UIColor * tapHighlightedColor; 37 | 38 | /** 39 | * 是否扩大点击范围,默认是打开 40 | */ 41 | @property (nonatomic, assign) BOOL enlargeTapArea; 42 | 43 | /** 44 | * 给文本添加点击事件Block回调 45 | * 46 | * @param strings 需要添加的字符串数组 47 | * @param tapClick 点击事件回调 48 | */ 49 | - (void)yb_addAttributeTapActionWithStrings:(NSArray *)strings 50 | tapClicked:(void (^) (UILabel * label, NSString *string, NSRange range, NSInteger index))tapClick; 51 | 52 | /** 53 | * 给文本添加点击事件delegate回调 54 | * 55 | * @param strings 需要添加的字符串数组 56 | * @param delegate delegate 57 | */ 58 | - (void)yb_addAttributeTapActionWithStrings:(NSArray *)strings 59 | delegate:(id )delegate; 60 | 61 | /** 62 | * 根据range给文本添加点击事件Block回调 63 | * 64 | * @param ranges 需要添加的Range字符串数组 65 | * @param tapClick 点击事件回调 66 | */ 67 | - (void)yb_addAttributeTapActionWithRanges:(NSArray *)ranges 68 | tapClicked:(void (^) (UILabel * label, NSString *string, NSRange range, NSInteger index))tapClick; 69 | 70 | /** 71 | * 根据range给文本添加点击事件delegate回调 72 | * 73 | * @param ranges 需要添加的Range字符串数组 74 | * @param delegate delegate 75 | */ 76 | - (void)yb_addAttributeTapActionWithRanges:(NSArray *)ranges 77 | delegate:(id )delegate; 78 | 79 | /** 80 | * 删除label上的点击事件 81 | */ 82 | - (void)yb_removeAttributeTapActions; 83 | 84 | @end 85 | 86 | -------------------------------------------------------------------------------- /UILabel+YBAttributeTapAction-Demo/YBAttributeTextTapAction/UILabel+YBAttributeTextTapAction.m: -------------------------------------------------------------------------------- 1 | // 2 | // UILabel+YBAttributeTextTapAction.m 3 | // 4 | // Created by LYB on 16/7/1. 5 | // Copyright © 2016年 LYB. All rights reserved. 6 | // 7 | 8 | #import "UILabel+YBAttributeTextTapAction.h" 9 | #import 10 | #import 11 | #import 12 | 13 | @interface YBAttributeModel : NSObject 14 | 15 | @property (nonatomic, copy) NSString *str; 16 | 17 | @property (nonatomic) NSRange range; 18 | 19 | @end 20 | 21 | @implementation YBAttributeModel 22 | 23 | @end 24 | 25 | @implementation UILabel (YBAttributeTextTapAction) 26 | 27 | #pragma mark - AssociatedObjects 28 | 29 | - (NSMutableArray *)attributeStrings 30 | { 31 | return objc_getAssociatedObject(self, _cmd); 32 | } 33 | 34 | - (void)setAttributeStrings:(NSMutableArray *)attributeStrings 35 | { 36 | objc_setAssociatedObject(self, @selector(attributeStrings), attributeStrings, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 37 | } 38 | 39 | - (NSMutableDictionary *)effectDic 40 | { 41 | return objc_getAssociatedObject(self, _cmd); 42 | } 43 | 44 | - (void)setEffectDic:(NSMutableDictionary *)effectDic 45 | { 46 | objc_setAssociatedObject(self, @selector(effectDic), effectDic, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 47 | } 48 | 49 | - (BOOL)isTapAction 50 | { 51 | return [objc_getAssociatedObject(self, _cmd) boolValue]; 52 | } 53 | 54 | - (void)setIsTapAction:(BOOL)isTapAction 55 | { 56 | objc_setAssociatedObject(self, @selector(isTapAction), @(isTapAction), OBJC_ASSOCIATION_ASSIGN); 57 | } 58 | 59 | - (void (^)(UILabel *, NSString *, NSRange, NSInteger))tapBlock 60 | { 61 | return objc_getAssociatedObject(self, _cmd); 62 | } 63 | 64 | - (void)setTapBlock:(void (^)(UILabel *, NSString *, NSRange, NSInteger))tapBlock 65 | { 66 | objc_setAssociatedObject(self, @selector(tapBlock), tapBlock, OBJC_ASSOCIATION_COPY_NONATOMIC); 67 | } 68 | 69 | - (id)delegate 70 | { 71 | return objc_getAssociatedObject(self, _cmd); 72 | } 73 | 74 | - (void)setDelegate:(id)delegate 75 | { 76 | objc_setAssociatedObject(self, @selector(delegate), delegate, OBJC_ASSOCIATION_ASSIGN); 77 | } 78 | 79 | - (BOOL)enabledTapEffect 80 | { 81 | return [objc_getAssociatedObject(self, _cmd) boolValue]; 82 | } 83 | 84 | - (void)setEnabledTapEffect:(BOOL)enabledTapEffect 85 | { 86 | objc_setAssociatedObject(self, @selector(enabledTapEffect), @(enabledTapEffect), OBJC_ASSOCIATION_ASSIGN); 87 | self.isTapEffect = enabledTapEffect; 88 | } 89 | 90 | - (BOOL)enlargeTapArea 91 | { 92 | NSNumber * number = objc_getAssociatedObject(self, _cmd); 93 | if (!number) { 94 | number = @(YES); 95 | objc_setAssociatedObject(self, _cmd, number, OBJC_ASSOCIATION_ASSIGN); 96 | } 97 | return [number boolValue]; 98 | } 99 | 100 | - (void)setEnlargeTapArea:(BOOL)enlargeTapArea 101 | { 102 | objc_setAssociatedObject(self, @selector(enlargeTapArea), @(enlargeTapArea), OBJC_ASSOCIATION_ASSIGN); 103 | } 104 | 105 | - (UIColor *)tapHighlightedColor 106 | { 107 | UIColor * color = objc_getAssociatedObject(self, _cmd); 108 | if (!color) { 109 | color = [UIColor lightGrayColor]; 110 | objc_setAssociatedObject(self, _cmd, color, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 111 | } 112 | return color; 113 | } 114 | 115 | - (void)setTapHighlightedColor:(UIColor *)tapHighlightedColor 116 | { 117 | objc_setAssociatedObject(self, @selector(tapHighlightedColor), tapHighlightedColor, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 118 | } 119 | 120 | - (BOOL)isTapEffect 121 | { 122 | return [objc_getAssociatedObject(self, _cmd) boolValue]; 123 | } 124 | 125 | - (void)setIsTapEffect:(BOOL)isTapEffect 126 | { 127 | objc_setAssociatedObject(self, @selector(isTapEffect), @(isTapEffect), OBJC_ASSOCIATION_ASSIGN); 128 | } 129 | 130 | #pragma mark - mainFunction 131 | - (void)yb_addAttributeTapActionWithStrings:(NSArray *)strings tapClicked:(void (^) (UILabel * label, NSString *string, NSRange range, NSInteger index))tapClick 132 | { 133 | [self yb_removeAttributeTapActions]; 134 | [self yb_getRangesWithStrings:strings]; 135 | self.userInteractionEnabled = YES; 136 | 137 | if (self.tapBlock != tapClick) { 138 | self.tapBlock = tapClick; 139 | } 140 | } 141 | 142 | - (void)yb_addAttributeTapActionWithStrings:(NSArray *)strings 143 | delegate:(id )delegate 144 | { 145 | [self yb_removeAttributeTapActions]; 146 | [self yb_getRangesWithStrings:strings]; 147 | self.userInteractionEnabled = YES; 148 | 149 | if (self.delegate != delegate) { 150 | self.delegate = delegate; 151 | } 152 | } 153 | 154 | - (void)yb_addAttributeTapActionWithRanges:(NSArray *)ranges tapClicked:(void (^)(UILabel *, NSString *, NSRange, NSInteger))tapClick 155 | { 156 | [self yb_removeAttributeTapActions]; 157 | [self yb_getRangesWithRanges:ranges]; 158 | self.userInteractionEnabled = YES; 159 | 160 | if (self.tapBlock != tapClick) { 161 | self.tapBlock = tapClick; 162 | } 163 | } 164 | 165 | - (void)yb_addAttributeTapActionWithRanges:(NSArray *)ranges delegate:(id)delegate 166 | { 167 | [self yb_removeAttributeTapActions]; 168 | [self yb_getRangesWithRanges:ranges]; 169 | self.userInteractionEnabled = YES; 170 | 171 | if (self.delegate != delegate) { 172 | self.delegate = delegate; 173 | } 174 | } 175 | 176 | - (void)yb_removeAttributeTapActions 177 | { 178 | self.tapBlock = nil; 179 | self.delegate = nil; 180 | self.effectDic = nil; 181 | self.isTapAction = NO; 182 | self.attributeStrings = [NSMutableArray array]; 183 | } 184 | 185 | #pragma mark - touchAction 186 | - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 187 | { 188 | if (!self.isTapAction) { 189 | [super touchesBegan:touches withEvent:event]; 190 | return; 191 | } 192 | 193 | if (objc_getAssociatedObject(self, @selector(enabledTapEffect))) { 194 | self.isTapEffect = self.enabledTapEffect; 195 | } 196 | 197 | UITouch *touch = [touches anyObject]; 198 | 199 | CGPoint point = [touch locationInView:self]; 200 | 201 | __weak typeof(self) weakSelf = self; 202 | 203 | BOOL ret = [self yb_getTapFrameWithTouchPoint:point result:^(NSString *string, NSRange range, NSInteger index) { 204 | 205 | if (weakSelf.isTapEffect) { 206 | 207 | [weakSelf yb_saveEffectDicWithRange:range]; 208 | 209 | [weakSelf yb_tapEffectWithStatus:YES]; 210 | } 211 | 212 | }]; 213 | if (!ret) { 214 | [super touchesBegan:touches withEvent:event]; 215 | } 216 | } 217 | 218 | - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event 219 | { 220 | if (!self.isTapAction) { 221 | [super touchesEnded:touches withEvent:event]; 222 | return; 223 | } 224 | if (self.isTapEffect) { 225 | [self performSelectorOnMainThread:@selector(yb_tapEffectWithStatus:) withObject:nil waitUntilDone:NO]; 226 | } 227 | 228 | UITouch *touch = [touches anyObject]; 229 | 230 | CGPoint point = [touch locationInView:self]; 231 | 232 | __weak typeof(self) weakSelf = self; 233 | 234 | BOOL ret = [self yb_getTapFrameWithTouchPoint:point result:^(NSString *string, NSRange range, NSInteger index) { 235 | if (weakSelf.tapBlock) { 236 | weakSelf.tapBlock (weakSelf, string, range, index); 237 | } 238 | 239 | if (weakSelf.delegate && [weakSelf.delegate respondsToSelector:@selector(yb_tapAttributeInLabel:string:range:index:)]) { 240 | [weakSelf.delegate yb_tapAttributeInLabel:weakSelf string:string range:range index:index]; 241 | } 242 | }]; 243 | if (!ret) { 244 | [super touchesEnded:touches withEvent:event]; 245 | } 246 | } 247 | 248 | - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event 249 | { 250 | if (!self.isTapAction) { 251 | [super touchesCancelled:touches withEvent:event]; 252 | return; 253 | } 254 | if (self.isTapEffect) { 255 | [self performSelectorOnMainThread:@selector(yb_tapEffectWithStatus:) withObject:nil waitUntilDone:NO]; 256 | } 257 | UITouch *touch = [touches anyObject]; 258 | 259 | CGPoint point = [touch locationInView:self]; 260 | 261 | __weak typeof(self) weakSelf = self; 262 | 263 | BOOL ret = [self yb_getTapFrameWithTouchPoint:point result:^(NSString *string, NSRange range, NSInteger index) { 264 | if (weakSelf.tapBlock) { 265 | weakSelf.tapBlock (weakSelf, string, range, index); 266 | } 267 | 268 | if (weakSelf.delegate && [weakSelf.delegate respondsToSelector:@selector(yb_tapAttributeInLabel:string:range:index:)]) { 269 | [weakSelf.delegate yb_tapAttributeInLabel:weakSelf string:string range:range index:index]; 270 | } 271 | }]; 272 | if (!ret) { 273 | [super touchesCancelled:touches withEvent:event]; 274 | } 275 | } 276 | 277 | #pragma mark - getTapFrame 278 | - (BOOL)yb_getTapFrameWithTouchPoint:(CGPoint)point result:(void (^) (NSString *string , NSRange range , NSInteger index))resultBlock 279 | { 280 | CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)self.attributedText); 281 | 282 | CGMutablePathRef Path = CGPathCreateMutable(); 283 | 284 | CGPathAddRect(Path, NULL, CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height + 20)); 285 | 286 | CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), Path, NULL); 287 | 288 | CFArrayRef lines = CTFrameGetLines(frame); 289 | 290 | CGFloat total_height = [self yb_textSizeWithAttributedString:self.attributedText width:self.bounds.size.width numberOfLines:0].height; 291 | 292 | if (!lines) { 293 | CFRelease(frame); 294 | CFRelease(framesetter); 295 | CGPathRelease(Path); 296 | return NO; 297 | } 298 | 299 | CFIndex count = CFArrayGetCount(lines); 300 | 301 | CGPoint origins[count]; 302 | 303 | CTFrameGetLineOrigins(frame, CFRangeMake(0, 0), origins); 304 | 305 | CGAffineTransform transform = [self yb_transformForCoreText]; 306 | 307 | for (CFIndex i = 0; i < count; i++) { 308 | CGPoint linePoint = origins[i]; 309 | 310 | CTLineRef line = CFArrayGetValueAtIndex(lines, i); 311 | 312 | CGRect flippedRect = [self yb_getLineBounds:line point:linePoint]; 313 | 314 | CGRect rect = CGRectApplyAffineTransform(flippedRect, transform); 315 | 316 | CGFloat lineOutSpace = (self.bounds.size.height - total_height) / 2; 317 | 318 | rect.origin.y = lineOutSpace + [self yb_getLineOrign:line]; 319 | 320 | if (self.enlargeTapArea) { 321 | rect.origin.y -= 5; 322 | rect.size.height += 10; 323 | } 324 | 325 | if (CGRectContainsPoint(rect, point)) { 326 | 327 | CGPoint relativePoint = CGPointMake(point.x - CGRectGetMinX(rect), point.y - CGRectGetMinY(rect)); 328 | 329 | CFIndex index = CTLineGetStringIndexForPosition(line, relativePoint); 330 | 331 | CGFloat offset; 332 | 333 | CTLineGetOffsetForStringIndex(line, index, &offset); 334 | 335 | if (offset > relativePoint.x) { 336 | index = index - 1; 337 | } 338 | 339 | NSInteger link_count = self.attributeStrings.count; 340 | 341 | for (int j = 0; j < link_count; j++) { 342 | 343 | YBAttributeModel *model = self.attributeStrings[j]; 344 | 345 | NSRange link_range = model.range; 346 | if (NSLocationInRange(index, link_range)) { 347 | if (resultBlock) { 348 | resultBlock (model.str , model.range , (NSInteger)j); 349 | } 350 | CFRelease(frame); 351 | CFRelease(framesetter); 352 | CGPathRelease(Path); 353 | return YES; 354 | } 355 | } 356 | } 357 | } 358 | CFRelease(frame); 359 | CFRelease(framesetter); 360 | CGPathRelease(Path); 361 | return NO; 362 | } 363 | 364 | - (CGAffineTransform)yb_transformForCoreText 365 | { 366 | return CGAffineTransformScale(CGAffineTransformMakeTranslation(0, self.bounds.size.height), 1.f, -1.f); 367 | } 368 | 369 | - (CGRect)yb_getLineBounds:(CTLineRef)line point:(CGPoint)point 370 | { 371 | CGFloat ascent = 0.0f; 372 | CGFloat descent = 0.0f; 373 | CGFloat leading = 0.0f; 374 | CGFloat width = (CGFloat)CTLineGetTypographicBounds(line, &ascent, &descent, &leading); 375 | CGFloat height = 0.0f; 376 | 377 | CFRange range = CTLineGetStringRange(line); 378 | NSAttributedString * attributedString = [self.attributedText attributedSubstringFromRange:NSMakeRange(range.location, range.length)]; 379 | if ([attributedString.string hasSuffix:@"\n"] && attributedString.string.length > 1) { 380 | attributedString = [attributedString attributedSubstringFromRange:NSMakeRange(0, attributedString.length - 1)]; 381 | } 382 | height = [self yb_textSizeWithAttributedString:attributedString width:self.bounds.size.width numberOfLines:0].height; 383 | return CGRectMake(point.x, point.y , width, height); 384 | } 385 | 386 | - (CGFloat)yb_getLineOrign:(CTLineRef)line 387 | { 388 | CFRange range = CTLineGetStringRange(line); 389 | if (range.location == 0) { 390 | return 0.; 391 | }else { 392 | NSAttributedString * attributedString = [self.attributedText attributedSubstringFromRange:NSMakeRange(0, range.location)]; 393 | if ([attributedString.string hasSuffix:@"\n"] && attributedString.string.length > 1) { 394 | attributedString = [attributedString attributedSubstringFromRange:NSMakeRange(0, attributedString.length - 1)]; 395 | } 396 | return [self yb_textSizeWithAttributedString:attributedString width:self.bounds.size.width numberOfLines:0].height; 397 | } 398 | } 399 | 400 | - (CGSize)yb_textSizeWithAttributedString:(NSAttributedString *)attributedString width:(float)width numberOfLines:(NSInteger)numberOfLines 401 | { 402 | @autoreleasepool { 403 | UILabel *sizeLabel = [[UILabel alloc] initWithFrame:CGRectZero]; 404 | sizeLabel.numberOfLines = numberOfLines; 405 | sizeLabel.attributedText = attributedString; 406 | CGSize fitSize = [sizeLabel sizeThatFits:CGSizeMake(width, MAXFLOAT)]; 407 | return fitSize; 408 | } 409 | } 410 | 411 | #pragma mark - tapEffect 412 | - (void)yb_tapEffectWithStatus:(BOOL)status 413 | { 414 | if (self.isTapEffect) { 415 | NSMutableAttributedString *attStr = [[NSMutableAttributedString alloc] initWithAttributedString:self.attributedText]; 416 | 417 | NSMutableAttributedString *subAtt = [[NSMutableAttributedString alloc] initWithAttributedString:[[self.effectDic allValues] firstObject]]; 418 | 419 | NSRange range = NSRangeFromString([[self.effectDic allKeys] firstObject]); 420 | 421 | if (status) { 422 | [subAtt addAttribute:NSBackgroundColorAttributeName value:self.tapHighlightedColor range:NSMakeRange(0, subAtt.string.length)]; 423 | 424 | [attStr replaceCharactersInRange:range withAttributedString:subAtt]; 425 | }else { 426 | 427 | [attStr replaceCharactersInRange:range withAttributedString:subAtt]; 428 | } 429 | self.attributedText = attStr; 430 | } 431 | } 432 | 433 | - (void)yb_saveEffectDicWithRange:(NSRange)range 434 | { 435 | self.effectDic = [NSMutableDictionary dictionary]; 436 | 437 | NSAttributedString *subAttribute = [self.attributedText attributedSubstringFromRange:range]; 438 | 439 | [self.effectDic setObject:subAttribute forKey:NSStringFromRange(range)]; 440 | } 441 | 442 | #pragma mark - getRange 443 | - (void)yb_getRangesWithStrings:(NSArray *)strings 444 | { 445 | if (self.attributedText == nil) { 446 | self.isTapAction = NO; 447 | return; 448 | } 449 | self.isTapAction = YES; 450 | self.isTapEffect = YES; 451 | __block NSString *totalStr = self.attributedText.string; 452 | self.attributeStrings = [NSMutableArray array]; 453 | __weak typeof(self) weakSelf = self; 454 | 455 | [strings enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 456 | 457 | NSRange range = [totalStr rangeOfString:obj]; 458 | 459 | if (range.length != 0) { 460 | 461 | totalStr = [totalStr stringByReplacingCharactersInRange:range withString:[weakSelf yb_getStringWithRange:range]]; 462 | 463 | YBAttributeModel *model = [YBAttributeModel new]; 464 | model.range = range; 465 | model.str = obj; 466 | [weakSelf.attributeStrings addObject:model]; 467 | 468 | } 469 | 470 | }]; 471 | } 472 | 473 | - (void)yb_getRangesWithRanges:(NSArray *)ranges 474 | { 475 | if (self.attributedText == nil) { 476 | self.isTapAction = NO; 477 | return; 478 | } 479 | 480 | self.isTapAction = YES; 481 | self.isTapEffect = YES; 482 | __block NSString *totalStr = self.attributedText.string; 483 | self.attributeStrings = [NSMutableArray array]; 484 | __weak typeof(self) weakSelf = self; 485 | 486 | [ranges enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 487 | NSRange range = NSRangeFromString(obj); 488 | NSAssert(totalStr.length >= range.location + range.length, @"NSRange(%ld,%ld) is out of bounds",range.location,range.length); 489 | NSString * string = [totalStr substringWithRange:range]; 490 | 491 | YBAttributeModel *model = [YBAttributeModel new]; 492 | model.range = range; 493 | model.str = string; 494 | [weakSelf.attributeStrings addObject:model]; 495 | }]; 496 | } 497 | 498 | - (NSString *)yb_getStringWithRange:(NSRange)range 499 | { 500 | NSMutableString *string = [NSMutableString string]; 501 | 502 | for (int i = 0; i < range.length ; i++) { 503 | 504 | [string appendString:@" "]; 505 | } 506 | return string; 507 | } 508 | 509 | #pragma mark - KVO 510 | - (void)yb_addObserver 511 | { 512 | [self addObserver:self forKeyPath:@"attributedText" options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) context:nil]; 513 | } 514 | 515 | - (void)yb_removeObserver 516 | { 517 | id info = self.observationInfo; 518 | NSString * key = @"attributedText"; 519 | NSArray *array = [info valueForKey:@"_observances"]; 520 | for (id objc in array) { 521 | id Properties = [objc valueForKeyPath:@"_property"]; 522 | NSString *keyPath = [Properties valueForKeyPath:@"_keyPath"]; 523 | if ([key isEqualToString:keyPath]) { 524 | [self removeObserver:self forKeyPath:@"attributedText" context:nil]; 525 | } 526 | } 527 | } 528 | 529 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context 530 | { 531 | if ([keyPath isEqualToString:@"attributedText"]) { 532 | if (self.isTapAction) { 533 | if (![change[NSKeyValueChangeNewKey] isEqual: change[NSKeyValueChangeOldKey]]) { 534 | 535 | } 536 | } 537 | } 538 | } 539 | 540 | @end 541 | -------------------------------------------------------------------------------- /UILabel+YBAttributeTapAction-Demo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // UILabel+YBAttributeTapAction-Demo 4 | // 5 | // Created by LYB on 16/7/1. 6 | // Copyright © 2016年 LYB. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /UILabel+YBAttributeTapAction-DemoTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /UILabel+YBAttributeTapAction-DemoTests/UILabel_YBAttributeTapAction_DemoTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // UILabel_YBAttributeTapAction_DemoTests.m 3 | // UILabel+YBAttributeTapAction-DemoTests 4 | // 5 | // Created by LYB on 16/7/1. 6 | // Copyright © 2016年 LYB. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface UILabel_YBAttributeTapAction_DemoTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation UILabel_YBAttributeTapAction_DemoTests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | // Put setup code here. This method is called before the invocation of each test method in the class. 20 | } 21 | 22 | - (void)tearDown { 23 | // Put teardown code here. This method is called after the invocation of each test method in the class. 24 | [super tearDown]; 25 | } 26 | 27 | - (void)testExample { 28 | // This is an example of a functional test case. 29 | // Use XCTAssert and related functions to verify your tests produce the correct results. 30 | } 31 | 32 | - (void)testPerformanceExample { 33 | // This is an example of a performance test case. 34 | [self measureBlock:^{ 35 | // Put the code you want to measure the time of here. 36 | }]; 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /UILabel+YBAttributeTapAction-DemoUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /UILabel+YBAttributeTapAction-DemoUITests/UILabel_YBAttributeTapAction_DemoUITests.m: -------------------------------------------------------------------------------- 1 | // 2 | // UILabel_YBAttributeTapAction_DemoUITests.m 3 | // UILabel+YBAttributeTapAction-DemoUITests 4 | // 5 | // Created by LYB on 16/7/1. 6 | // Copyright © 2016年 LYB. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface UILabel_YBAttributeTapAction_DemoUITests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation UILabel_YBAttributeTapAction_DemoUITests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | 22 | // In UI tests it is usually best to stop immediately when a failure occurs. 23 | self.continueAfterFailure = NO; 24 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 25 | [[[XCUIApplication alloc] init] launch]; 26 | 27 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 28 | } 29 | 30 | - (void)tearDown { 31 | // Put teardown code here. This method is called after the invocation of each test method in the class. 32 | [super tearDown]; 33 | } 34 | 35 | - (void)testExample { 36 | // Use recording to get started writing UI tests. 37 | // Use XCTAssert and related functions to verify your tests produce the correct results. 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /YBAttributeTextTapAction.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "YBAttributeTextTapAction" 3 | s.version = "3.0.3" 4 | s.summary = "一行代码添加文本点击事件/a fast way to implement click event text" 5 | s.description = "一行代码添加文本点击事件/a fast way to implement click event text Code updated by Lyb." 6 | s.homepage = "https://github.com/lyb5834/YBAttributeTextTapAction.git" 7 | s.license = "MIT" 8 | s.author = { "lyb" => "lyb5834@126.com" } 9 | s.source = { :git => "https://github.com/lyb5834/YBAttributeTextTapAction.git", :tag => s.version.to_s } 10 | s.source_files = "YBAttributeTextTapAction/*.{h,m}" 11 | s.requires_arc = true 12 | s.platform = :ios, '6.0' 13 | end 14 | -------------------------------------------------------------------------------- /YBAttributeTextTapAction/UILabel+YBAttributeTextTapAction.h: -------------------------------------------------------------------------------- 1 | // 2 | // UILabel+YBAttributeTextTapAction.h 3 | // 4 | // Created by LYB on 16/7/1. 5 | // Copyright © 2016年 LYB. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | @protocol YBAttributeTapActionDelegate 11 | @optional 12 | /** 13 | * YBAttributeTapActionDelegate 14 | * 15 | * @param string 点击的字符串 16 | * @param range 点击的字符串range 17 | * @param index 点击的字符在数组中的index 18 | */ 19 | - (void)yb_tapAttributeInLabel:(UILabel *)label 20 | string:(NSString *)string 21 | range:(NSRange)range 22 | index:(NSInteger)index; 23 | @end 24 | 25 | 26 | @interface UILabel (YBAttributeTextTapAction) 27 | 28 | /** 29 | * 是否打开点击效果,默认是打开 30 | */ 31 | @property (nonatomic, assign) BOOL enabledTapEffect; 32 | 33 | /** 34 | * 点击高亮色 默认是[UIColor lightGrayColor] 需打开enabledTapEffect才有效 35 | */ 36 | @property (nonatomic, strong) UIColor * tapHighlightedColor; 37 | 38 | /** 39 | * 是否扩大点击范围,默认是打开 40 | */ 41 | @property (nonatomic, assign) BOOL enlargeTapArea; 42 | 43 | /** 44 | * 给文本添加点击事件Block回调 45 | * 46 | * @param strings 需要添加的字符串数组 47 | * @param tapClick 点击事件回调 48 | */ 49 | - (void)yb_addAttributeTapActionWithStrings:(NSArray *)strings 50 | tapClicked:(void (^) (UILabel * label, NSString *string, NSRange range, NSInteger index))tapClick; 51 | 52 | /** 53 | * 给文本添加点击事件delegate回调 54 | * 55 | * @param strings 需要添加的字符串数组 56 | * @param delegate delegate 57 | */ 58 | - (void)yb_addAttributeTapActionWithStrings:(NSArray *)strings 59 | delegate:(id )delegate; 60 | 61 | /** 62 | * 根据range给文本添加点击事件Block回调 63 | * 64 | * @param ranges 需要添加的Range字符串数组 65 | * @param tapClick 点击事件回调 66 | */ 67 | - (void)yb_addAttributeTapActionWithRanges:(NSArray *)ranges 68 | tapClicked:(void (^) (UILabel * label, NSString *string, NSRange range, NSInteger index))tapClick; 69 | 70 | /** 71 | * 根据range给文本添加点击事件delegate回调 72 | * 73 | * @param ranges 需要添加的Range字符串数组 74 | * @param delegate delegate 75 | */ 76 | - (void)yb_addAttributeTapActionWithRanges:(NSArray *)ranges 77 | delegate:(id )delegate; 78 | 79 | /** 80 | * 删除label上的点击事件 81 | */ 82 | - (void)yb_removeAttributeTapActions; 83 | 84 | @end 85 | 86 | -------------------------------------------------------------------------------- /YBAttributeTextTapAction/UILabel+YBAttributeTextTapAction.m: -------------------------------------------------------------------------------- 1 | // 2 | // UILabel+YBAttributeTextTapAction.m 3 | // 4 | // Created by LYB on 16/7/1. 5 | // Copyright © 2016年 LYB. All rights reserved. 6 | // 7 | 8 | #import "UILabel+YBAttributeTextTapAction.h" 9 | #import 10 | #import 11 | #import 12 | 13 | @interface YBAttributeModel : NSObject 14 | 15 | @property (nonatomic, copy) NSString *str; 16 | 17 | @property (nonatomic) NSRange range; 18 | 19 | @end 20 | 21 | @implementation YBAttributeModel 22 | 23 | @end 24 | 25 | @implementation UILabel (YBAttributeTextTapAction) 26 | 27 | #pragma mark - AssociatedObjects 28 | 29 | - (NSMutableArray *)attributeStrings 30 | { 31 | return objc_getAssociatedObject(self, _cmd); 32 | } 33 | 34 | - (void)setAttributeStrings:(NSMutableArray *)attributeStrings 35 | { 36 | objc_setAssociatedObject(self, @selector(attributeStrings), attributeStrings, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 37 | } 38 | 39 | - (NSMutableDictionary *)effectDic 40 | { 41 | return objc_getAssociatedObject(self, _cmd); 42 | } 43 | 44 | - (void)setEffectDic:(NSMutableDictionary *)effectDic 45 | { 46 | objc_setAssociatedObject(self, @selector(effectDic), effectDic, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 47 | } 48 | 49 | - (BOOL)isTapAction 50 | { 51 | return [objc_getAssociatedObject(self, _cmd) boolValue]; 52 | } 53 | 54 | - (void)setIsTapAction:(BOOL)isTapAction 55 | { 56 | objc_setAssociatedObject(self, @selector(isTapAction), @(isTapAction), OBJC_ASSOCIATION_ASSIGN); 57 | } 58 | 59 | - (void (^)(UILabel *, NSString *, NSRange, NSInteger))tapBlock 60 | { 61 | return objc_getAssociatedObject(self, _cmd); 62 | } 63 | 64 | - (void)setTapBlock:(void (^)(UILabel *, NSString *, NSRange, NSInteger))tapBlock 65 | { 66 | objc_setAssociatedObject(self, @selector(tapBlock), tapBlock, OBJC_ASSOCIATION_COPY_NONATOMIC); 67 | } 68 | 69 | - (id)delegate 70 | { 71 | return objc_getAssociatedObject(self, _cmd); 72 | } 73 | 74 | - (void)setDelegate:(id)delegate 75 | { 76 | objc_setAssociatedObject(self, @selector(delegate), delegate, OBJC_ASSOCIATION_ASSIGN); 77 | } 78 | 79 | - (BOOL)enabledTapEffect 80 | { 81 | return [objc_getAssociatedObject(self, _cmd) boolValue]; 82 | } 83 | 84 | - (void)setEnabledTapEffect:(BOOL)enabledTapEffect 85 | { 86 | objc_setAssociatedObject(self, @selector(enabledTapEffect), @(enabledTapEffect), OBJC_ASSOCIATION_ASSIGN); 87 | self.isTapEffect = enabledTapEffect; 88 | } 89 | 90 | - (BOOL)enlargeTapArea 91 | { 92 | NSNumber * number = objc_getAssociatedObject(self, _cmd); 93 | if (!number) { 94 | number = @(YES); 95 | objc_setAssociatedObject(self, _cmd, number, OBJC_ASSOCIATION_ASSIGN); 96 | } 97 | return [number boolValue]; 98 | } 99 | 100 | - (void)setEnlargeTapArea:(BOOL)enlargeTapArea 101 | { 102 | objc_setAssociatedObject(self, @selector(enlargeTapArea), @(enlargeTapArea), OBJC_ASSOCIATION_ASSIGN); 103 | } 104 | 105 | - (UIColor *)tapHighlightedColor 106 | { 107 | UIColor * color = objc_getAssociatedObject(self, _cmd); 108 | if (!color) { 109 | color = [UIColor lightGrayColor]; 110 | objc_setAssociatedObject(self, _cmd, color, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 111 | } 112 | return color; 113 | } 114 | 115 | - (void)setTapHighlightedColor:(UIColor *)tapHighlightedColor 116 | { 117 | objc_setAssociatedObject(self, @selector(tapHighlightedColor), tapHighlightedColor, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 118 | } 119 | 120 | - (BOOL)isTapEffect 121 | { 122 | return [objc_getAssociatedObject(self, _cmd) boolValue]; 123 | } 124 | 125 | - (void)setIsTapEffect:(BOOL)isTapEffect 126 | { 127 | objc_setAssociatedObject(self, @selector(isTapEffect), @(isTapEffect), OBJC_ASSOCIATION_ASSIGN); 128 | } 129 | 130 | #pragma mark - mainFunction 131 | - (void)yb_addAttributeTapActionWithStrings:(NSArray *)strings tapClicked:(void (^) (UILabel * label, NSString *string, NSRange range, NSInteger index))tapClick 132 | { 133 | [self yb_removeAttributeTapActions]; 134 | [self yb_getRangesWithStrings:strings]; 135 | self.userInteractionEnabled = YES; 136 | 137 | if (self.tapBlock != tapClick) { 138 | self.tapBlock = tapClick; 139 | } 140 | } 141 | 142 | - (void)yb_addAttributeTapActionWithStrings:(NSArray *)strings 143 | delegate:(id )delegate 144 | { 145 | [self yb_removeAttributeTapActions]; 146 | [self yb_getRangesWithStrings:strings]; 147 | self.userInteractionEnabled = YES; 148 | 149 | if (self.delegate != delegate) { 150 | self.delegate = delegate; 151 | } 152 | } 153 | 154 | - (void)yb_addAttributeTapActionWithRanges:(NSArray *)ranges tapClicked:(void (^)(UILabel *, NSString *, NSRange, NSInteger))tapClick 155 | { 156 | [self yb_removeAttributeTapActions]; 157 | [self yb_getRangesWithRanges:ranges]; 158 | self.userInteractionEnabled = YES; 159 | 160 | if (self.tapBlock != tapClick) { 161 | self.tapBlock = tapClick; 162 | } 163 | } 164 | 165 | - (void)yb_addAttributeTapActionWithRanges:(NSArray *)ranges delegate:(id)delegate 166 | { 167 | [self yb_removeAttributeTapActions]; 168 | [self yb_getRangesWithRanges:ranges]; 169 | self.userInteractionEnabled = YES; 170 | 171 | if (self.delegate != delegate) { 172 | self.delegate = delegate; 173 | } 174 | } 175 | 176 | - (void)yb_removeAttributeTapActions 177 | { 178 | self.tapBlock = nil; 179 | self.delegate = nil; 180 | self.effectDic = nil; 181 | self.isTapAction = NO; 182 | self.attributeStrings = [NSMutableArray array]; 183 | } 184 | 185 | #pragma mark - touchAction 186 | - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 187 | { 188 | if (!self.isTapAction) { 189 | [super touchesBegan:touches withEvent:event]; 190 | return; 191 | } 192 | 193 | if (objc_getAssociatedObject(self, @selector(enabledTapEffect))) { 194 | self.isTapEffect = self.enabledTapEffect; 195 | } 196 | 197 | UITouch *touch = [touches anyObject]; 198 | 199 | CGPoint point = [touch locationInView:self]; 200 | 201 | __weak typeof(self) weakSelf = self; 202 | 203 | BOOL ret = [self yb_getTapFrameWithTouchPoint:point result:^(NSString *string, NSRange range, NSInteger index) { 204 | 205 | if (weakSelf.isTapEffect) { 206 | 207 | [weakSelf yb_saveEffectDicWithRange:range]; 208 | 209 | [weakSelf yb_tapEffectWithStatus:YES]; 210 | } 211 | 212 | }]; 213 | if (!ret) { 214 | [super touchesBegan:touches withEvent:event]; 215 | } 216 | } 217 | 218 | - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event 219 | { 220 | if (!self.isTapAction) { 221 | [super touchesEnded:touches withEvent:event]; 222 | return; 223 | } 224 | if (self.isTapEffect) { 225 | [self performSelectorOnMainThread:@selector(yb_tapEffectWithStatus:) withObject:nil waitUntilDone:NO]; 226 | } 227 | 228 | UITouch *touch = [touches anyObject]; 229 | 230 | CGPoint point = [touch locationInView:self]; 231 | 232 | __weak typeof(self) weakSelf = self; 233 | 234 | BOOL ret = [self yb_getTapFrameWithTouchPoint:point result:^(NSString *string, NSRange range, NSInteger index) { 235 | if (weakSelf.tapBlock) { 236 | weakSelf.tapBlock (weakSelf, string, range, index); 237 | } 238 | 239 | if (weakSelf.delegate && [weakSelf.delegate respondsToSelector:@selector(yb_tapAttributeInLabel:string:range:index:)]) { 240 | [weakSelf.delegate yb_tapAttributeInLabel:weakSelf string:string range:range index:index]; 241 | } 242 | }]; 243 | if (!ret) { 244 | [super touchesEnded:touches withEvent:event]; 245 | } 246 | } 247 | 248 | - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event 249 | { 250 | if (!self.isTapAction) { 251 | [super touchesCancelled:touches withEvent:event]; 252 | return; 253 | } 254 | if (self.isTapEffect) { 255 | [self performSelectorOnMainThread:@selector(yb_tapEffectWithStatus:) withObject:nil waitUntilDone:NO]; 256 | } 257 | UITouch *touch = [touches anyObject]; 258 | 259 | CGPoint point = [touch locationInView:self]; 260 | 261 | __weak typeof(self) weakSelf = self; 262 | 263 | BOOL ret = [self yb_getTapFrameWithTouchPoint:point result:^(NSString *string, NSRange range, NSInteger index) { 264 | if (weakSelf.tapBlock) { 265 | weakSelf.tapBlock (weakSelf, string, range, index); 266 | } 267 | 268 | if (weakSelf.delegate && [weakSelf.delegate respondsToSelector:@selector(yb_tapAttributeInLabel:string:range:index:)]) { 269 | [weakSelf.delegate yb_tapAttributeInLabel:weakSelf string:string range:range index:index]; 270 | } 271 | }]; 272 | if (!ret) { 273 | [super touchesCancelled:touches withEvent:event]; 274 | } 275 | } 276 | 277 | #pragma mark - getTapFrame 278 | - (BOOL)yb_getTapFrameWithTouchPoint:(CGPoint)point result:(void (^) (NSString *string , NSRange range , NSInteger index))resultBlock 279 | { 280 | CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)self.attributedText); 281 | 282 | CGMutablePathRef Path = CGPathCreateMutable(); 283 | 284 | CGPathAddRect(Path, NULL, CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height + 20)); 285 | 286 | CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), Path, NULL); 287 | 288 | CFArrayRef lines = CTFrameGetLines(frame); 289 | 290 | CGFloat total_height = [self yb_textSizeWithAttributedString:self.attributedText width:self.bounds.size.width numberOfLines:0].height; 291 | 292 | if (!lines) { 293 | CFRelease(frame); 294 | CFRelease(framesetter); 295 | CGPathRelease(Path); 296 | return NO; 297 | } 298 | 299 | CFIndex count = CFArrayGetCount(lines); 300 | 301 | CGPoint origins[count]; 302 | 303 | CTFrameGetLineOrigins(frame, CFRangeMake(0, 0), origins); 304 | 305 | CGAffineTransform transform = [self yb_transformForCoreText]; 306 | 307 | for (CFIndex i = 0; i < count; i++) { 308 | CGPoint linePoint = origins[i]; 309 | 310 | CTLineRef line = CFArrayGetValueAtIndex(lines, i); 311 | 312 | CGRect flippedRect = [self yb_getLineBounds:line point:linePoint]; 313 | 314 | CGRect rect = CGRectApplyAffineTransform(flippedRect, transform); 315 | 316 | CGFloat lineOutSpace = (self.bounds.size.height - total_height) / 2; 317 | 318 | rect.origin.y = lineOutSpace + [self yb_getLineOrign:line]; 319 | 320 | if (self.enlargeTapArea) { 321 | rect.origin.y -= 5; 322 | rect.size.height += 10; 323 | } 324 | 325 | if (CGRectContainsPoint(rect, point)) { 326 | 327 | CGPoint relativePoint = CGPointMake(point.x - CGRectGetMinX(rect), point.y - CGRectGetMinY(rect)); 328 | 329 | CFIndex index = CTLineGetStringIndexForPosition(line, relativePoint); 330 | 331 | CGFloat offset; 332 | 333 | CTLineGetOffsetForStringIndex(line, index, &offset); 334 | 335 | if (offset > relativePoint.x) { 336 | index = index - 1; 337 | } 338 | 339 | NSInteger link_count = self.attributeStrings.count; 340 | 341 | for (int j = 0; j < link_count; j++) { 342 | 343 | YBAttributeModel *model = self.attributeStrings[j]; 344 | 345 | NSRange link_range = model.range; 346 | if (NSLocationInRange(index, link_range)) { 347 | if (resultBlock) { 348 | resultBlock (model.str , model.range , (NSInteger)j); 349 | } 350 | CFRelease(frame); 351 | CFRelease(framesetter); 352 | CGPathRelease(Path); 353 | return YES; 354 | } 355 | } 356 | } 357 | } 358 | CFRelease(frame); 359 | CFRelease(framesetter); 360 | CGPathRelease(Path); 361 | return NO; 362 | } 363 | 364 | - (CGAffineTransform)yb_transformForCoreText 365 | { 366 | return CGAffineTransformScale(CGAffineTransformMakeTranslation(0, self.bounds.size.height), 1.f, -1.f); 367 | } 368 | 369 | - (CGRect)yb_getLineBounds:(CTLineRef)line point:(CGPoint)point 370 | { 371 | CGFloat ascent = 0.0f; 372 | CGFloat descent = 0.0f; 373 | CGFloat leading = 0.0f; 374 | CGFloat width = (CGFloat)CTLineGetTypographicBounds(line, &ascent, &descent, &leading); 375 | CGFloat height = 0.0f; 376 | 377 | CFRange range = CTLineGetStringRange(line); 378 | NSAttributedString * attributedString = [self.attributedText attributedSubstringFromRange:NSMakeRange(range.location, range.length)]; 379 | if ([attributedString.string hasSuffix:@"\n"] && attributedString.string.length > 1) { 380 | attributedString = [attributedString attributedSubstringFromRange:NSMakeRange(0, attributedString.length - 1)]; 381 | } 382 | height = [self yb_textSizeWithAttributedString:attributedString width:self.bounds.size.width numberOfLines:0].height; 383 | return CGRectMake(point.x, point.y , width, height); 384 | } 385 | 386 | - (CGFloat)yb_getLineOrign:(CTLineRef)line 387 | { 388 | CFRange range = CTLineGetStringRange(line); 389 | if (range.location == 0) { 390 | return 0.; 391 | }else { 392 | NSAttributedString * attributedString = [self.attributedText attributedSubstringFromRange:NSMakeRange(0, range.location)]; 393 | if ([attributedString.string hasSuffix:@"\n"] && attributedString.string.length > 1) { 394 | attributedString = [attributedString attributedSubstringFromRange:NSMakeRange(0, attributedString.length - 1)]; 395 | } 396 | return [self yb_textSizeWithAttributedString:attributedString width:self.bounds.size.width numberOfLines:0].height; 397 | } 398 | } 399 | 400 | - (CGSize)yb_textSizeWithAttributedString:(NSAttributedString *)attributedString width:(float)width numberOfLines:(NSInteger)numberOfLines 401 | { 402 | @autoreleasepool { 403 | UILabel *sizeLabel = [[UILabel alloc] initWithFrame:CGRectZero]; 404 | sizeLabel.numberOfLines = numberOfLines; 405 | sizeLabel.attributedText = attributedString; 406 | CGSize fitSize = [sizeLabel sizeThatFits:CGSizeMake(width, MAXFLOAT)]; 407 | return fitSize; 408 | } 409 | } 410 | 411 | #pragma mark - tapEffect 412 | - (void)yb_tapEffectWithStatus:(BOOL)status 413 | { 414 | if (self.isTapEffect) { 415 | NSMutableAttributedString *attStr = [[NSMutableAttributedString alloc] initWithAttributedString:self.attributedText]; 416 | 417 | NSMutableAttributedString *subAtt = [[NSMutableAttributedString alloc] initWithAttributedString:[[self.effectDic allValues] firstObject]]; 418 | 419 | NSRange range = NSRangeFromString([[self.effectDic allKeys] firstObject]); 420 | 421 | if (status) { 422 | [subAtt addAttribute:NSBackgroundColorAttributeName value:self.tapHighlightedColor range:NSMakeRange(0, subAtt.string.length)]; 423 | 424 | [attStr replaceCharactersInRange:range withAttributedString:subAtt]; 425 | }else { 426 | 427 | [attStr replaceCharactersInRange:range withAttributedString:subAtt]; 428 | } 429 | self.attributedText = attStr; 430 | } 431 | } 432 | 433 | - (void)yb_saveEffectDicWithRange:(NSRange)range 434 | { 435 | self.effectDic = [NSMutableDictionary dictionary]; 436 | 437 | NSAttributedString *subAttribute = [self.attributedText attributedSubstringFromRange:range]; 438 | 439 | [self.effectDic setObject:subAttribute forKey:NSStringFromRange(range)]; 440 | } 441 | 442 | #pragma mark - getRange 443 | - (void)yb_getRangesWithStrings:(NSArray *)strings 444 | { 445 | if (self.attributedText == nil) { 446 | self.isTapAction = NO; 447 | return; 448 | } 449 | self.isTapAction = YES; 450 | self.isTapEffect = YES; 451 | __block NSString *totalStr = self.attributedText.string; 452 | self.attributeStrings = [NSMutableArray array]; 453 | __weak typeof(self) weakSelf = self; 454 | 455 | [strings enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 456 | 457 | NSRange range = [totalStr rangeOfString:obj]; 458 | 459 | if (range.length != 0) { 460 | 461 | totalStr = [totalStr stringByReplacingCharactersInRange:range withString:[weakSelf yb_getStringWithRange:range]]; 462 | 463 | YBAttributeModel *model = [YBAttributeModel new]; 464 | model.range = range; 465 | model.str = obj; 466 | [weakSelf.attributeStrings addObject:model]; 467 | 468 | } 469 | 470 | }]; 471 | } 472 | 473 | - (void)yb_getRangesWithRanges:(NSArray *)ranges 474 | { 475 | if (self.attributedText == nil) { 476 | self.isTapAction = NO; 477 | return; 478 | } 479 | 480 | self.isTapAction = YES; 481 | self.isTapEffect = YES; 482 | __block NSString *totalStr = self.attributedText.string; 483 | self.attributeStrings = [NSMutableArray array]; 484 | __weak typeof(self) weakSelf = self; 485 | 486 | [ranges enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 487 | NSRange range = NSRangeFromString(obj); 488 | NSAssert(totalStr.length >= range.location + range.length, @"NSRange(%ld,%ld) is out of bounds",range.location,range.length); 489 | NSString * string = [totalStr substringWithRange:range]; 490 | 491 | YBAttributeModel *model = [YBAttributeModel new]; 492 | model.range = range; 493 | model.str = string; 494 | [weakSelf.attributeStrings addObject:model]; 495 | }]; 496 | } 497 | 498 | - (NSString *)yb_getStringWithRange:(NSRange)range 499 | { 500 | NSMutableString *string = [NSMutableString string]; 501 | 502 | for (int i = 0; i < range.length ; i++) { 503 | 504 | [string appendString:@" "]; 505 | } 506 | return string; 507 | } 508 | 509 | #pragma mark - KVO 510 | - (void)yb_addObserver 511 | { 512 | [self addObserver:self forKeyPath:@"attributedText" options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) context:nil]; 513 | } 514 | 515 | - (void)yb_removeObserver 516 | { 517 | id info = self.observationInfo; 518 | NSString * key = @"attributedText"; 519 | NSArray *array = [info valueForKey:@"_observances"]; 520 | for (id objc in array) { 521 | id Properties = [objc valueForKeyPath:@"_property"]; 522 | NSString *keyPath = [Properties valueForKeyPath:@"_keyPath"]; 523 | if ([key isEqualToString:keyPath]) { 524 | [self removeObserver:self forKeyPath:@"attributedText" context:nil]; 525 | } 526 | } 527 | } 528 | 529 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context 530 | { 531 | if ([keyPath isEqualToString:@"attributedText"]) { 532 | if (self.isTapAction) { 533 | if (![change[NSKeyValueChangeNewKey] isEqual: change[NSKeyValueChangeOldKey]]) { 534 | 535 | } 536 | } 537 | } 538 | } 539 | 540 | @end 541 | -------------------------------------------------------------------------------- /attributeAction.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lyb5834/YBAttributeTextTapAction/dbb3b856cb9ed9cedbcd70df09537f655638728f/attributeAction.gif --------------------------------------------------------------------------------