├── .gitignore ├── README.md ├── YXClickTextView ├── YXClickTextView.h └── YXClickTextView.m ├── YXClickTextViewDemo.xcodeproj └── project.pbxproj ├── YXClickTextViewDemo ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Info.plist ├── ViewController.h ├── ViewController.m ├── YXClickTextView │ ├── YXClickTextView.h │ └── YXClickTextView.m ├── YXKitAssist │ ├── YXKitAssist.h │ └── YXKitAssist.m └── main.m ├── YXClickTextViewDemoTests ├── Info.plist └── YXClickTextViewDemoTests.m ├── YXClickTextViewDemoUITests ├── Info.plist └── YXClickTextViewDemoUITests.m ├── YXKitAssist ├── YXKitAssist.h └── YXKitAssist.m └── example.jpg /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | 3 | .DS_Store 4 | 5 | ## Build generated 6 | 7 | build/ 8 | Index/ 9 | 10 | DerivedData/ 11 | 12 | ## Various settings 13 | 14 | *.pbxuser 15 | 16 | !default.pbxuser 17 | 18 | *.mode1v3 19 | 20 | !default.mode1v3 21 | 22 | *.mode2v3 23 | 24 | !default.mode2v3 25 | 26 | *.perspectivev3 27 | 28 | !default.perspectivev3 29 | 30 | xcuserdata/ 31 | 32 | ## Other 33 | 34 | *.moved-aside 35 | 36 | *.xccheckout 37 | 38 | *.xcworkspace 39 | 40 | !default.xcworkspace 41 | 42 | ## Obj-C/Swift specific 43 | 44 | *.hmap 45 | 46 | *.ipa 47 | 48 | *.dSYM.zip 49 | 50 | *.dSYM 51 | 52 | # CocoaPods 53 | 54 | Pods 55 | 56 | !Podfile 57 | 58 | !Podfile.lock 59 | 60 | # Carthage 61 | 62 | Carthage/Build 63 | 64 | # fastlane 65 | 66 | fastlane/report.xml 67 | 68 | fastlane/Preview.html 69 | 70 | fastlane/screenshots 71 | 72 | fastlane/test_output 73 | 74 | # Code Injection 75 | 76 | iOSInjectionProject/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # YXClickTextView 2 | 一个简单的可点击 textView 3 | 4 | 主要应用场景: 5 | 6 | * 各种服务协议的点击 7 | 8 | * 一些简单的富文本应用 9 | 10 | 示例图: 11 | ![图](https://github.com/sinexy/YXClickTextView/blob/master/example.jpg) 12 | 13 | 使用示例: 14 | ``` 15 | YXClickTextView *textView1 = [[YXClickTextView alloc] init]; 16 | textView1.frame = CGRectMake(20, 140, 300, 30); 17 | textView1.text = @"您已阅读并同意《注册与服务协议》"; 18 | textView1.yxkit_assist.attributedSubstring(@"《注册与服务协议》", [UIColor blueColor]); 19 | [textView1 addClickTexts:@[@"《注册与服务协议》",] callback:^(NSString *text, NSRange range) { 20 | NSString *msg = [NSString stringWithFormat:@"text:%@,range:%@",text,NSStringFromRange(range)]; 21 | NSLog(@"%@",msg); 22 | }]; 23 | ``` 24 | 25 | 在 block 中加入点击事件的响应即可 26 | 27 | -------------------------------------------------------------------------------- /YXClickTextView/YXClickTextView.h: -------------------------------------------------------------------------------- 1 | // 2 | // YXClickTextView.h 3 | // Forst 4 | // 5 | // Created by yunxin bai on 2018/6/26. 6 | // Copyright © 2018 yunxin bai. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "YXKitAssist.h" 11 | 12 | @class YXClickTextView; 13 | 14 | @protocol YXClickTextViewDelegate 15 | 16 | @optional 17 | - (void)clickTextView:(YXClickTextView *)clickTextView didClickedText:(NSString *)text range:(NSRange)range; 18 | 19 | @end 20 | 21 | typedef void(^YXClickTextBlock)(NSString *text, NSRange range); 22 | 23 | @interface YXClickTextView : UITextView 24 | 25 | @property (nonatomic, weak) id yxDelegate; 26 | 27 | @property (nonatomic, assign) BOOL autoHeight; 28 | 29 | @property (nonatomic, assign) BOOL autoWidth; 30 | 31 | @property (nonatomic, strong) UIColor *highlightedBackgroundColor; 32 | 33 | - (void)addClickTexts:(NSArray *)texts callback:(YXClickTextBlock)callback; 34 | 35 | - (void)addClickTexts:(NSArray *)texts; 36 | 37 | - (void)removeClickText:(NSArray *)texts; 38 | 39 | - (void)removeAllClickText; 40 | 41 | @end 42 | 43 | -------------------------------------------------------------------------------- /YXClickTextView/YXClickTextView.m: -------------------------------------------------------------------------------- 1 | // 2 | // YXClickTextView.m 3 | // Forst 4 | // 5 | // Created by yunxin bai on 2018/6/26. 6 | // Copyright © 2018 yunxin bai. All rights reserved. 7 | // 8 | 9 | #import "YXClickTextView.h" 10 | 11 | @interface YXClickTextModel : NSObject 12 | 13 | @property (nonatomic, copy) NSString *text; 14 | @property (nonatomic, assign) NSRange range; 15 | @property (nonatomic, copy) YXClickTextBlock textBlock; 16 | @property (nonatomic, strong) UIColor *color; 17 | @property (nonatomic, strong) UIFont *font; 18 | 19 | @end 20 | 21 | @implementation YXClickTextModel 22 | @end 23 | 24 | 25 | @interface YXClickTextView () 26 | 27 | @property (nonatomic, strong) NSMutableArray *textArrayM; 28 | @property (nonatomic, strong) YXClickTextModel *model; 29 | 30 | @end 31 | 32 | @implementation YXClickTextView 33 | 34 | - (instancetype)initWithFrame:(CGRect)frame { 35 | self = [super initWithFrame:frame]; 36 | if (self) { 37 | self.editable = NO; 38 | self.scrollEnabled = NO; 39 | self.textContainerInset = UIEdgeInsetsZero; 40 | self.showsVerticalScrollIndicator = NO; 41 | self.showsHorizontalScrollIndicator = NO; 42 | self.textContainer.lineFragmentPadding = 0; 43 | self.selectable = NO; 44 | 45 | _autoHeight = YES; 46 | _autoWidth = NO; 47 | } 48 | return self; 49 | } 50 | 51 | #pragma mark - override 52 | - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { 53 | NSUInteger location = [self characterIndexAtLocation:[touches.anyObject locationInView:self]]; 54 | if (location == NSNotFound) { 55 | return [super touchesBegan:touches withEvent:event]; 56 | } 57 | } 58 | 59 | - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { 60 | NSUInteger location = [self characterIndexAtLocation:[touches.anyObject locationInView:self]]; 61 | if (location == NSNotFound) { 62 | return [super touchesBegan:touches withEvent:event]; 63 | } 64 | } 65 | 66 | - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { 67 | [self shouldCallBack]; 68 | } 69 | 70 | - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { 71 | [self shouldCallBack]; 72 | } 73 | 74 | - (void)willMoveToSuperview:(UIView *)newSuperview { 75 | [super willMoveToSuperview:newSuperview]; 76 | if (newSuperview) { 77 | UIView *hoderView = [newSuperview viewWithTag:10086]; 78 | if (!hoderView) { 79 | hoderView = [[UIView alloc] init]; 80 | hoderView.tag = 10086; 81 | [newSuperview addSubview:hoderView]; 82 | } 83 | } 84 | } 85 | 86 | #pragma mark - private 87 | - (NSUInteger)characterIndexAtLocation:(CGPoint)point { 88 | NSUInteger index = [self.layoutManager glyphIndexForPoint:point inTextContainer:self.textContainer]; 89 | CGRect rect = [self.layoutManager boundingRectForGlyphRange:NSMakeRange(index, 1) inTextContainer:self.textContainer]; 90 | 91 | YXClickTextModel *selectModel = nil; 92 | if (CGRectContainsPoint(rect, point)) { 93 | for (YXClickTextModel *model in self.textArrayM) { 94 | if (index >= model.range.location && index < model.range.location + model.range.length) { 95 | selectModel = model; 96 | break; 97 | } 98 | } 99 | } 100 | if (selectModel) { 101 | self.model = selectModel; 102 | }else { 103 | self.model = nil; 104 | } 105 | if (_highlightedBackgroundColor) { 106 | [self changeClickTextBackgroundColor:self.backgroundColor type:0]; 107 | } 108 | 109 | return NSNotFound; 110 | } 111 | 112 | - (void)shouldCallBack { 113 | if (_model.textBlock) { 114 | _model.textBlock(_model.text, _model.range); 115 | } 116 | if (_model && _yxDelegate && [_yxDelegate respondsToSelector:@selector(clickTextView:didClickedText:range:)]) { 117 | [_yxDelegate clickTextView:self didClickedText:_model.text range:_model.range]; 118 | } 119 | _model = nil; 120 | } 121 | 122 | - (void)allRangeOfText:(NSString *)text inText:(NSString *)theText callBack:(YXClickTextBlock)callback { 123 | NSRange range = [theText rangeOfString:text]; 124 | while (range.location != NSNotFound) { 125 | YXClickTextModel *model = [YXClickTextModel new]; 126 | model.text = text; 127 | model.range = range; 128 | model.textBlock = callback; 129 | [self.textArrayM addObject:model]; 130 | NSUInteger length = range.location + range.length; 131 | range = [theText rangeOfString:text options:kNilOptions range:NSMakeRange(length, theText.length - length)]; 132 | } 133 | } 134 | 135 | - (void)changeClickTextBackgroundColor:(UIColor *)bgcolor type:(NSInteger)type { 136 | if (bgcolor == nil) { 137 | bgcolor = [UIColor clearColor]; 138 | } 139 | if (_model) { 140 | NSMutableAttributedString *mastring = [[NSMutableAttributedString alloc] initWithAttributedString:self.attributedText?:[[NSAttributedString alloc] initWithString:self.text]]; 141 | if (type == 0) { 142 | [mastring addAttribute:NSBackgroundColorAttributeName value:bgcolor range:_model.range]; 143 | }else if (type == 1) { 144 | [mastring removeAttribute:NSBackgroundColorAttributeName range:_model.range]; 145 | } 146 | self.attributedText = mastring; 147 | } 148 | } 149 | 150 | #pragma mark - public method 151 | - (void)addClickTexts:(NSArray *)texts callback:(YXClickTextBlock)callback { 152 | NSString *theText = self.text; 153 | dispatch_async(dispatch_get_global_queue(0, 0), ^{ 154 | if (texts && callback) { 155 | for (NSString *text in texts) { 156 | if (text.length == 0) { 157 | continue; 158 | } 159 | [self allRangeOfText:text inText:theText callBack:callback]; 160 | } 161 | } 162 | }); 163 | } 164 | 165 | - (void)addClickTexts:(NSArray *)texts { 166 | [self addClickTexts:texts callback:nil]; 167 | } 168 | 169 | - (void)removeClickText:(NSArray *)texts { 170 | NSMutableArray *marr = @[].mutableCopy; 171 | for (NSString *text in texts) { 172 | if (text.length == 0) { 173 | continue; 174 | } 175 | for (YXClickTextModel *model in _textArrayM) { 176 | if ([model.text isEqualToString:text]) { 177 | [marr addObject:model]; 178 | } 179 | } 180 | } 181 | [self.textArrayM removeObjectsInArray:marr]; 182 | } 183 | 184 | - (void)removeAllClickText { 185 | [self.textArrayM removeAllObjects]; 186 | } 187 | 188 | #pragma mark - setters and getters 189 | - (NSMutableArray *)textArrayM { 190 | if (!_textArrayM) { 191 | _textArrayM = [NSMutableArray array]; 192 | } 193 | return _textArrayM; 194 | } 195 | 196 | - (void)setText:(NSString *)text { 197 | [super setText:text]; 198 | if (_autoWidth && _autoHeight) { 199 | [self sizeToFit]; 200 | }else if (_autoHeight) { 201 | CGSize size = [self sizeThatFits:self.bounds.size]; 202 | CGRect frame = self.frame; 203 | frame.size.height = size.height; 204 | self.frame = frame; 205 | }else if (_autoWidth) { 206 | CGSize size = [self sizeThatFits:self.bounds.size]; 207 | CGRect frame = self.frame; 208 | frame.size.width = size.width; 209 | self.frame = frame; 210 | } 211 | } 212 | @end 213 | -------------------------------------------------------------------------------- /YXClickTextViewDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | F78B40FC2255D204000BE07B /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = F78B40FB2255D204000BE07B /* AppDelegate.m */; }; 11 | F78B40FF2255D204000BE07B /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F78B40FE2255D204000BE07B /* ViewController.m */; }; 12 | F78B41022255D204000BE07B /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F78B41002255D204000BE07B /* Main.storyboard */; }; 13 | F78B41042255D206000BE07B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F78B41032255D206000BE07B /* Assets.xcassets */; }; 14 | F78B41072255D206000BE07B /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F78B41052255D206000BE07B /* LaunchScreen.storyboard */; }; 15 | F78B410A2255D206000BE07B /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = F78B41092255D206000BE07B /* main.m */; }; 16 | F78B41142255D207000BE07B /* YXClickTextViewDemoTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F78B41132255D207000BE07B /* YXClickTextViewDemoTests.m */; }; 17 | F78B411F2255D207000BE07B /* YXClickTextViewDemoUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = F78B411E2255D207000BE07B /* YXClickTextViewDemoUITests.m */; }; 18 | F78B412F2255D22D000BE07B /* YXClickTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = F78B412E2255D22D000BE07B /* YXClickTextView.m */; }; 19 | F78B41332255D6C1000BE07B /* YXKitAssist.m in Sources */ = {isa = PBXBuildFile; fileRef = F78B41322255D6C1000BE07B /* YXKitAssist.m */; }; 20 | /* End PBXBuildFile section */ 21 | 22 | /* Begin PBXContainerItemProxy section */ 23 | F78B41102255D207000BE07B /* PBXContainerItemProxy */ = { 24 | isa = PBXContainerItemProxy; 25 | containerPortal = F78B40EF2255D204000BE07B /* Project object */; 26 | proxyType = 1; 27 | remoteGlobalIDString = F78B40F62255D204000BE07B; 28 | remoteInfo = YXClickTextViewDemo; 29 | }; 30 | F78B411B2255D207000BE07B /* PBXContainerItemProxy */ = { 31 | isa = PBXContainerItemProxy; 32 | containerPortal = F78B40EF2255D204000BE07B /* Project object */; 33 | proxyType = 1; 34 | remoteGlobalIDString = F78B40F62255D204000BE07B; 35 | remoteInfo = YXClickTextViewDemo; 36 | }; 37 | /* End PBXContainerItemProxy section */ 38 | 39 | /* Begin PBXFileReference section */ 40 | F78B40F72255D204000BE07B /* YXClickTextViewDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = YXClickTextViewDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 41 | F78B40FA2255D204000BE07B /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 42 | F78B40FB2255D204000BE07B /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 43 | F78B40FD2255D204000BE07B /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 44 | F78B40FE2255D204000BE07B /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 45 | F78B41012255D204000BE07B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 46 | F78B41032255D206000BE07B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 47 | F78B41062255D206000BE07B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 48 | F78B41082255D206000BE07B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 49 | F78B41092255D206000BE07B /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 50 | F78B410F2255D207000BE07B /* YXClickTextViewDemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = YXClickTextViewDemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 51 | F78B41132255D207000BE07B /* YXClickTextViewDemoTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = YXClickTextViewDemoTests.m; sourceTree = ""; }; 52 | F78B41152255D207000BE07B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 53 | F78B411A2255D207000BE07B /* YXClickTextViewDemoUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = YXClickTextViewDemoUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 54 | F78B411E2255D207000BE07B /* YXClickTextViewDemoUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = YXClickTextViewDemoUITests.m; sourceTree = ""; }; 55 | F78B41202255D207000BE07B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 56 | F78B412D2255D22D000BE07B /* YXClickTextView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = YXClickTextView.h; sourceTree = ""; }; 57 | F78B412E2255D22D000BE07B /* YXClickTextView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = YXClickTextView.m; sourceTree = ""; }; 58 | F78B41312255D6C1000BE07B /* YXKitAssist.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = YXKitAssist.h; sourceTree = ""; }; 59 | F78B41322255D6C1000BE07B /* YXKitAssist.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = YXKitAssist.m; sourceTree = ""; }; 60 | /* End PBXFileReference section */ 61 | 62 | /* Begin PBXFrameworksBuildPhase section */ 63 | F78B40F42255D204000BE07B /* Frameworks */ = { 64 | isa = PBXFrameworksBuildPhase; 65 | buildActionMask = 2147483647; 66 | files = ( 67 | ); 68 | runOnlyForDeploymentPostprocessing = 0; 69 | }; 70 | F78B410C2255D207000BE07B /* Frameworks */ = { 71 | isa = PBXFrameworksBuildPhase; 72 | buildActionMask = 2147483647; 73 | files = ( 74 | ); 75 | runOnlyForDeploymentPostprocessing = 0; 76 | }; 77 | F78B41172255D207000BE07B /* Frameworks */ = { 78 | isa = PBXFrameworksBuildPhase; 79 | buildActionMask = 2147483647; 80 | files = ( 81 | ); 82 | runOnlyForDeploymentPostprocessing = 0; 83 | }; 84 | /* End PBXFrameworksBuildPhase section */ 85 | 86 | /* Begin PBXGroup section */ 87 | F78B40EE2255D204000BE07B = { 88 | isa = PBXGroup; 89 | children = ( 90 | F78B40F92255D204000BE07B /* YXClickTextViewDemo */, 91 | F78B41122255D207000BE07B /* YXClickTextViewDemoTests */, 92 | F78B411D2255D207000BE07B /* YXClickTextViewDemoUITests */, 93 | F78B40F82255D204000BE07B /* Products */, 94 | ); 95 | sourceTree = ""; 96 | }; 97 | F78B40F82255D204000BE07B /* Products */ = { 98 | isa = PBXGroup; 99 | children = ( 100 | F78B40F72255D204000BE07B /* YXClickTextViewDemo.app */, 101 | F78B410F2255D207000BE07B /* YXClickTextViewDemoTests.xctest */, 102 | F78B411A2255D207000BE07B /* YXClickTextViewDemoUITests.xctest */, 103 | ); 104 | name = Products; 105 | sourceTree = ""; 106 | }; 107 | F78B40F92255D204000BE07B /* YXClickTextViewDemo */ = { 108 | isa = PBXGroup; 109 | children = ( 110 | F78B41302255D667000BE07B /* YXKitAssist */, 111 | F78B412C2255D21C000BE07B /* YXClickTextView */, 112 | F78B40FA2255D204000BE07B /* AppDelegate.h */, 113 | F78B40FB2255D204000BE07B /* AppDelegate.m */, 114 | F78B40FD2255D204000BE07B /* ViewController.h */, 115 | F78B40FE2255D204000BE07B /* ViewController.m */, 116 | F78B41002255D204000BE07B /* Main.storyboard */, 117 | F78B41032255D206000BE07B /* Assets.xcassets */, 118 | F78B41052255D206000BE07B /* LaunchScreen.storyboard */, 119 | F78B41082255D206000BE07B /* Info.plist */, 120 | F78B41092255D206000BE07B /* main.m */, 121 | ); 122 | path = YXClickTextViewDemo; 123 | sourceTree = ""; 124 | }; 125 | F78B41122255D207000BE07B /* YXClickTextViewDemoTests */ = { 126 | isa = PBXGroup; 127 | children = ( 128 | F78B41132255D207000BE07B /* YXClickTextViewDemoTests.m */, 129 | F78B41152255D207000BE07B /* Info.plist */, 130 | ); 131 | path = YXClickTextViewDemoTests; 132 | sourceTree = ""; 133 | }; 134 | F78B411D2255D207000BE07B /* YXClickTextViewDemoUITests */ = { 135 | isa = PBXGroup; 136 | children = ( 137 | F78B411E2255D207000BE07B /* YXClickTextViewDemoUITests.m */, 138 | F78B41202255D207000BE07B /* Info.plist */, 139 | ); 140 | path = YXClickTextViewDemoUITests; 141 | sourceTree = ""; 142 | }; 143 | F78B412C2255D21C000BE07B /* YXClickTextView */ = { 144 | isa = PBXGroup; 145 | children = ( 146 | F78B412D2255D22D000BE07B /* YXClickTextView.h */, 147 | F78B412E2255D22D000BE07B /* YXClickTextView.m */, 148 | ); 149 | path = YXClickTextView; 150 | sourceTree = ""; 151 | }; 152 | F78B41302255D667000BE07B /* YXKitAssist */ = { 153 | isa = PBXGroup; 154 | children = ( 155 | F78B41312255D6C1000BE07B /* YXKitAssist.h */, 156 | F78B41322255D6C1000BE07B /* YXKitAssist.m */, 157 | ); 158 | path = YXKitAssist; 159 | sourceTree = ""; 160 | }; 161 | /* End PBXGroup section */ 162 | 163 | /* Begin PBXNativeTarget section */ 164 | F78B40F62255D204000BE07B /* YXClickTextViewDemo */ = { 165 | isa = PBXNativeTarget; 166 | buildConfigurationList = F78B41232255D207000BE07B /* Build configuration list for PBXNativeTarget "YXClickTextViewDemo" */; 167 | buildPhases = ( 168 | F78B40F32255D204000BE07B /* Sources */, 169 | F78B40F42255D204000BE07B /* Frameworks */, 170 | F78B40F52255D204000BE07B /* Resources */, 171 | ); 172 | buildRules = ( 173 | ); 174 | dependencies = ( 175 | ); 176 | name = YXClickTextViewDemo; 177 | productName = YXClickTextViewDemo; 178 | productReference = F78B40F72255D204000BE07B /* YXClickTextViewDemo.app */; 179 | productType = "com.apple.product-type.application"; 180 | }; 181 | F78B410E2255D207000BE07B /* YXClickTextViewDemoTests */ = { 182 | isa = PBXNativeTarget; 183 | buildConfigurationList = F78B41262255D207000BE07B /* Build configuration list for PBXNativeTarget "YXClickTextViewDemoTests" */; 184 | buildPhases = ( 185 | F78B410B2255D207000BE07B /* Sources */, 186 | F78B410C2255D207000BE07B /* Frameworks */, 187 | F78B410D2255D207000BE07B /* Resources */, 188 | ); 189 | buildRules = ( 190 | ); 191 | dependencies = ( 192 | F78B41112255D207000BE07B /* PBXTargetDependency */, 193 | ); 194 | name = YXClickTextViewDemoTests; 195 | productName = YXClickTextViewDemoTests; 196 | productReference = F78B410F2255D207000BE07B /* YXClickTextViewDemoTests.xctest */; 197 | productType = "com.apple.product-type.bundle.unit-test"; 198 | }; 199 | F78B41192255D207000BE07B /* YXClickTextViewDemoUITests */ = { 200 | isa = PBXNativeTarget; 201 | buildConfigurationList = F78B41292255D207000BE07B /* Build configuration list for PBXNativeTarget "YXClickTextViewDemoUITests" */; 202 | buildPhases = ( 203 | F78B41162255D207000BE07B /* Sources */, 204 | F78B41172255D207000BE07B /* Frameworks */, 205 | F78B41182255D207000BE07B /* Resources */, 206 | ); 207 | buildRules = ( 208 | ); 209 | dependencies = ( 210 | F78B411C2255D207000BE07B /* PBXTargetDependency */, 211 | ); 212 | name = YXClickTextViewDemoUITests; 213 | productName = YXClickTextViewDemoUITests; 214 | productReference = F78B411A2255D207000BE07B /* YXClickTextViewDemoUITests.xctest */; 215 | productType = "com.apple.product-type.bundle.ui-testing"; 216 | }; 217 | /* End PBXNativeTarget section */ 218 | 219 | /* Begin PBXProject section */ 220 | F78B40EF2255D204000BE07B /* Project object */ = { 221 | isa = PBXProject; 222 | attributes = { 223 | LastUpgradeCheck = 1010; 224 | ORGANIZATIONNAME = "yunxin bai"; 225 | TargetAttributes = { 226 | F78B40F62255D204000BE07B = { 227 | CreatedOnToolsVersion = 10.1; 228 | }; 229 | F78B410E2255D207000BE07B = { 230 | CreatedOnToolsVersion = 10.1; 231 | TestTargetID = F78B40F62255D204000BE07B; 232 | }; 233 | F78B41192255D207000BE07B = { 234 | CreatedOnToolsVersion = 10.1; 235 | TestTargetID = F78B40F62255D204000BE07B; 236 | }; 237 | }; 238 | }; 239 | buildConfigurationList = F78B40F22255D204000BE07B /* Build configuration list for PBXProject "YXClickTextViewDemo" */; 240 | compatibilityVersion = "Xcode 9.3"; 241 | developmentRegion = en; 242 | hasScannedForEncodings = 0; 243 | knownRegions = ( 244 | en, 245 | Base, 246 | ); 247 | mainGroup = F78B40EE2255D204000BE07B; 248 | productRefGroup = F78B40F82255D204000BE07B /* Products */; 249 | projectDirPath = ""; 250 | projectRoot = ""; 251 | targets = ( 252 | F78B40F62255D204000BE07B /* YXClickTextViewDemo */, 253 | F78B410E2255D207000BE07B /* YXClickTextViewDemoTests */, 254 | F78B41192255D207000BE07B /* YXClickTextViewDemoUITests */, 255 | ); 256 | }; 257 | /* End PBXProject section */ 258 | 259 | /* Begin PBXResourcesBuildPhase section */ 260 | F78B40F52255D204000BE07B /* Resources */ = { 261 | isa = PBXResourcesBuildPhase; 262 | buildActionMask = 2147483647; 263 | files = ( 264 | F78B41072255D206000BE07B /* LaunchScreen.storyboard in Resources */, 265 | F78B41042255D206000BE07B /* Assets.xcassets in Resources */, 266 | F78B41022255D204000BE07B /* Main.storyboard in Resources */, 267 | ); 268 | runOnlyForDeploymentPostprocessing = 0; 269 | }; 270 | F78B410D2255D207000BE07B /* Resources */ = { 271 | isa = PBXResourcesBuildPhase; 272 | buildActionMask = 2147483647; 273 | files = ( 274 | ); 275 | runOnlyForDeploymentPostprocessing = 0; 276 | }; 277 | F78B41182255D207000BE07B /* Resources */ = { 278 | isa = PBXResourcesBuildPhase; 279 | buildActionMask = 2147483647; 280 | files = ( 281 | ); 282 | runOnlyForDeploymentPostprocessing = 0; 283 | }; 284 | /* End PBXResourcesBuildPhase section */ 285 | 286 | /* Begin PBXSourcesBuildPhase section */ 287 | F78B40F32255D204000BE07B /* Sources */ = { 288 | isa = PBXSourcesBuildPhase; 289 | buildActionMask = 2147483647; 290 | files = ( 291 | F78B40FF2255D204000BE07B /* ViewController.m in Sources */, 292 | F78B410A2255D206000BE07B /* main.m in Sources */, 293 | F78B40FC2255D204000BE07B /* AppDelegate.m in Sources */, 294 | F78B41332255D6C1000BE07B /* YXKitAssist.m in Sources */, 295 | F78B412F2255D22D000BE07B /* YXClickTextView.m in Sources */, 296 | ); 297 | runOnlyForDeploymentPostprocessing = 0; 298 | }; 299 | F78B410B2255D207000BE07B /* Sources */ = { 300 | isa = PBXSourcesBuildPhase; 301 | buildActionMask = 2147483647; 302 | files = ( 303 | F78B41142255D207000BE07B /* YXClickTextViewDemoTests.m in Sources */, 304 | ); 305 | runOnlyForDeploymentPostprocessing = 0; 306 | }; 307 | F78B41162255D207000BE07B /* Sources */ = { 308 | isa = PBXSourcesBuildPhase; 309 | buildActionMask = 2147483647; 310 | files = ( 311 | F78B411F2255D207000BE07B /* YXClickTextViewDemoUITests.m in Sources */, 312 | ); 313 | runOnlyForDeploymentPostprocessing = 0; 314 | }; 315 | /* End PBXSourcesBuildPhase section */ 316 | 317 | /* Begin PBXTargetDependency section */ 318 | F78B41112255D207000BE07B /* PBXTargetDependency */ = { 319 | isa = PBXTargetDependency; 320 | target = F78B40F62255D204000BE07B /* YXClickTextViewDemo */; 321 | targetProxy = F78B41102255D207000BE07B /* PBXContainerItemProxy */; 322 | }; 323 | F78B411C2255D207000BE07B /* PBXTargetDependency */ = { 324 | isa = PBXTargetDependency; 325 | target = F78B40F62255D204000BE07B /* YXClickTextViewDemo */; 326 | targetProxy = F78B411B2255D207000BE07B /* PBXContainerItemProxy */; 327 | }; 328 | /* End PBXTargetDependency section */ 329 | 330 | /* Begin PBXVariantGroup section */ 331 | F78B41002255D204000BE07B /* Main.storyboard */ = { 332 | isa = PBXVariantGroup; 333 | children = ( 334 | F78B41012255D204000BE07B /* Base */, 335 | ); 336 | name = Main.storyboard; 337 | sourceTree = ""; 338 | }; 339 | F78B41052255D206000BE07B /* LaunchScreen.storyboard */ = { 340 | isa = PBXVariantGroup; 341 | children = ( 342 | F78B41062255D206000BE07B /* Base */, 343 | ); 344 | name = LaunchScreen.storyboard; 345 | sourceTree = ""; 346 | }; 347 | /* End PBXVariantGroup section */ 348 | 349 | /* Begin XCBuildConfiguration section */ 350 | F78B41212255D207000BE07B /* Debug */ = { 351 | isa = XCBuildConfiguration; 352 | buildSettings = { 353 | ALWAYS_SEARCH_USER_PATHS = NO; 354 | CLANG_ANALYZER_NONNULL = YES; 355 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 356 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 357 | CLANG_CXX_LIBRARY = "libc++"; 358 | CLANG_ENABLE_MODULES = YES; 359 | CLANG_ENABLE_OBJC_ARC = YES; 360 | CLANG_ENABLE_OBJC_WEAK = YES; 361 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 362 | CLANG_WARN_BOOL_CONVERSION = YES; 363 | CLANG_WARN_COMMA = YES; 364 | CLANG_WARN_CONSTANT_CONVERSION = YES; 365 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 366 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 367 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 368 | CLANG_WARN_EMPTY_BODY = YES; 369 | CLANG_WARN_ENUM_CONVERSION = YES; 370 | CLANG_WARN_INFINITE_RECURSION = YES; 371 | CLANG_WARN_INT_CONVERSION = YES; 372 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 373 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 374 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 375 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 376 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 377 | CLANG_WARN_STRICT_PROTOTYPES = YES; 378 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 379 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 380 | CLANG_WARN_UNREACHABLE_CODE = YES; 381 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 382 | CODE_SIGN_IDENTITY = "iPhone Developer"; 383 | COPY_PHASE_STRIP = NO; 384 | DEBUG_INFORMATION_FORMAT = dwarf; 385 | ENABLE_STRICT_OBJC_MSGSEND = YES; 386 | ENABLE_TESTABILITY = YES; 387 | GCC_C_LANGUAGE_STANDARD = gnu11; 388 | GCC_DYNAMIC_NO_PIC = NO; 389 | GCC_NO_COMMON_BLOCKS = YES; 390 | GCC_OPTIMIZATION_LEVEL = 0; 391 | GCC_PREPROCESSOR_DEFINITIONS = ( 392 | "DEBUG=1", 393 | "$(inherited)", 394 | ); 395 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 396 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 397 | GCC_WARN_UNDECLARED_SELECTOR = YES; 398 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 399 | GCC_WARN_UNUSED_FUNCTION = YES; 400 | GCC_WARN_UNUSED_VARIABLE = YES; 401 | IPHONEOS_DEPLOYMENT_TARGET = 12.1; 402 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 403 | MTL_FAST_MATH = YES; 404 | ONLY_ACTIVE_ARCH = YES; 405 | SDKROOT = iphoneos; 406 | }; 407 | name = Debug; 408 | }; 409 | F78B41222255D207000BE07B /* Release */ = { 410 | isa = XCBuildConfiguration; 411 | buildSettings = { 412 | ALWAYS_SEARCH_USER_PATHS = NO; 413 | CLANG_ANALYZER_NONNULL = YES; 414 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 415 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 416 | CLANG_CXX_LIBRARY = "libc++"; 417 | CLANG_ENABLE_MODULES = YES; 418 | CLANG_ENABLE_OBJC_ARC = YES; 419 | CLANG_ENABLE_OBJC_WEAK = YES; 420 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 421 | CLANG_WARN_BOOL_CONVERSION = YES; 422 | CLANG_WARN_COMMA = YES; 423 | CLANG_WARN_CONSTANT_CONVERSION = YES; 424 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 425 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 426 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 427 | CLANG_WARN_EMPTY_BODY = YES; 428 | CLANG_WARN_ENUM_CONVERSION = YES; 429 | CLANG_WARN_INFINITE_RECURSION = YES; 430 | CLANG_WARN_INT_CONVERSION = YES; 431 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 432 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 433 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 434 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 435 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 436 | CLANG_WARN_STRICT_PROTOTYPES = YES; 437 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 438 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 439 | CLANG_WARN_UNREACHABLE_CODE = YES; 440 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 441 | CODE_SIGN_IDENTITY = "iPhone Developer"; 442 | COPY_PHASE_STRIP = NO; 443 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 444 | ENABLE_NS_ASSERTIONS = NO; 445 | ENABLE_STRICT_OBJC_MSGSEND = YES; 446 | GCC_C_LANGUAGE_STANDARD = gnu11; 447 | GCC_NO_COMMON_BLOCKS = YES; 448 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 449 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 450 | GCC_WARN_UNDECLARED_SELECTOR = YES; 451 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 452 | GCC_WARN_UNUSED_FUNCTION = YES; 453 | GCC_WARN_UNUSED_VARIABLE = YES; 454 | IPHONEOS_DEPLOYMENT_TARGET = 12.1; 455 | MTL_ENABLE_DEBUG_INFO = NO; 456 | MTL_FAST_MATH = YES; 457 | SDKROOT = iphoneos; 458 | VALIDATE_PRODUCT = YES; 459 | }; 460 | name = Release; 461 | }; 462 | F78B41242255D207000BE07B /* Debug */ = { 463 | isa = XCBuildConfiguration; 464 | buildSettings = { 465 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 466 | CODE_SIGN_STYLE = Automatic; 467 | DEVELOPMENT_TEAM = JAZ7464V6H; 468 | INFOPLIST_FILE = YXClickTextViewDemo/Info.plist; 469 | LD_RUNPATH_SEARCH_PATHS = ( 470 | "$(inherited)", 471 | "@executable_path/Frameworks", 472 | ); 473 | PRODUCT_BUNDLE_IDENTIFIER = com.baiyunxin..YXClickTextViewDemo; 474 | PRODUCT_NAME = "$(TARGET_NAME)"; 475 | TARGETED_DEVICE_FAMILY = "1,2"; 476 | }; 477 | name = Debug; 478 | }; 479 | F78B41252255D207000BE07B /* Release */ = { 480 | isa = XCBuildConfiguration; 481 | buildSettings = { 482 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 483 | CODE_SIGN_STYLE = Automatic; 484 | DEVELOPMENT_TEAM = JAZ7464V6H; 485 | INFOPLIST_FILE = YXClickTextViewDemo/Info.plist; 486 | LD_RUNPATH_SEARCH_PATHS = ( 487 | "$(inherited)", 488 | "@executable_path/Frameworks", 489 | ); 490 | PRODUCT_BUNDLE_IDENTIFIER = com.baiyunxin..YXClickTextViewDemo; 491 | PRODUCT_NAME = "$(TARGET_NAME)"; 492 | TARGETED_DEVICE_FAMILY = "1,2"; 493 | }; 494 | name = Release; 495 | }; 496 | F78B41272255D207000BE07B /* Debug */ = { 497 | isa = XCBuildConfiguration; 498 | buildSettings = { 499 | BUNDLE_LOADER = "$(TEST_HOST)"; 500 | CODE_SIGN_STYLE = Automatic; 501 | DEVELOPMENT_TEAM = JAZ7464V6H; 502 | INFOPLIST_FILE = YXClickTextViewDemoTests/Info.plist; 503 | LD_RUNPATH_SEARCH_PATHS = ( 504 | "$(inherited)", 505 | "@executable_path/Frameworks", 506 | "@loader_path/Frameworks", 507 | ); 508 | PRODUCT_BUNDLE_IDENTIFIER = com.baiyunxin..YXClickTextViewDemoTests; 509 | PRODUCT_NAME = "$(TARGET_NAME)"; 510 | TARGETED_DEVICE_FAMILY = "1,2"; 511 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/YXClickTextViewDemo.app/YXClickTextViewDemo"; 512 | }; 513 | name = Debug; 514 | }; 515 | F78B41282255D207000BE07B /* Release */ = { 516 | isa = XCBuildConfiguration; 517 | buildSettings = { 518 | BUNDLE_LOADER = "$(TEST_HOST)"; 519 | CODE_SIGN_STYLE = Automatic; 520 | DEVELOPMENT_TEAM = JAZ7464V6H; 521 | INFOPLIST_FILE = YXClickTextViewDemoTests/Info.plist; 522 | LD_RUNPATH_SEARCH_PATHS = ( 523 | "$(inherited)", 524 | "@executable_path/Frameworks", 525 | "@loader_path/Frameworks", 526 | ); 527 | PRODUCT_BUNDLE_IDENTIFIER = com.baiyunxin..YXClickTextViewDemoTests; 528 | PRODUCT_NAME = "$(TARGET_NAME)"; 529 | TARGETED_DEVICE_FAMILY = "1,2"; 530 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/YXClickTextViewDemo.app/YXClickTextViewDemo"; 531 | }; 532 | name = Release; 533 | }; 534 | F78B412A2255D207000BE07B /* Debug */ = { 535 | isa = XCBuildConfiguration; 536 | buildSettings = { 537 | CODE_SIGN_STYLE = Automatic; 538 | DEVELOPMENT_TEAM = JAZ7464V6H; 539 | INFOPLIST_FILE = YXClickTextViewDemoUITests/Info.plist; 540 | LD_RUNPATH_SEARCH_PATHS = ( 541 | "$(inherited)", 542 | "@executable_path/Frameworks", 543 | "@loader_path/Frameworks", 544 | ); 545 | PRODUCT_BUNDLE_IDENTIFIER = com.baiyunxin..YXClickTextViewDemoUITests; 546 | PRODUCT_NAME = "$(TARGET_NAME)"; 547 | TARGETED_DEVICE_FAMILY = "1,2"; 548 | TEST_TARGET_NAME = YXClickTextViewDemo; 549 | }; 550 | name = Debug; 551 | }; 552 | F78B412B2255D207000BE07B /* Release */ = { 553 | isa = XCBuildConfiguration; 554 | buildSettings = { 555 | CODE_SIGN_STYLE = Automatic; 556 | DEVELOPMENT_TEAM = JAZ7464V6H; 557 | INFOPLIST_FILE = YXClickTextViewDemoUITests/Info.plist; 558 | LD_RUNPATH_SEARCH_PATHS = ( 559 | "$(inherited)", 560 | "@executable_path/Frameworks", 561 | "@loader_path/Frameworks", 562 | ); 563 | PRODUCT_BUNDLE_IDENTIFIER = com.baiyunxin..YXClickTextViewDemoUITests; 564 | PRODUCT_NAME = "$(TARGET_NAME)"; 565 | TARGETED_DEVICE_FAMILY = "1,2"; 566 | TEST_TARGET_NAME = YXClickTextViewDemo; 567 | }; 568 | name = Release; 569 | }; 570 | /* End XCBuildConfiguration section */ 571 | 572 | /* Begin XCConfigurationList section */ 573 | F78B40F22255D204000BE07B /* Build configuration list for PBXProject "YXClickTextViewDemo" */ = { 574 | isa = XCConfigurationList; 575 | buildConfigurations = ( 576 | F78B41212255D207000BE07B /* Debug */, 577 | F78B41222255D207000BE07B /* Release */, 578 | ); 579 | defaultConfigurationIsVisible = 0; 580 | defaultConfigurationName = Release; 581 | }; 582 | F78B41232255D207000BE07B /* Build configuration list for PBXNativeTarget "YXClickTextViewDemo" */ = { 583 | isa = XCConfigurationList; 584 | buildConfigurations = ( 585 | F78B41242255D207000BE07B /* Debug */, 586 | F78B41252255D207000BE07B /* Release */, 587 | ); 588 | defaultConfigurationIsVisible = 0; 589 | defaultConfigurationName = Release; 590 | }; 591 | F78B41262255D207000BE07B /* Build configuration list for PBXNativeTarget "YXClickTextViewDemoTests" */ = { 592 | isa = XCConfigurationList; 593 | buildConfigurations = ( 594 | F78B41272255D207000BE07B /* Debug */, 595 | F78B41282255D207000BE07B /* Release */, 596 | ); 597 | defaultConfigurationIsVisible = 0; 598 | defaultConfigurationName = Release; 599 | }; 600 | F78B41292255D207000BE07B /* Build configuration list for PBXNativeTarget "YXClickTextViewDemoUITests" */ = { 601 | isa = XCConfigurationList; 602 | buildConfigurations = ( 603 | F78B412A2255D207000BE07B /* Debug */, 604 | F78B412B2255D207000BE07B /* Release */, 605 | ); 606 | defaultConfigurationIsVisible = 0; 607 | defaultConfigurationName = Release; 608 | }; 609 | /* End XCConfigurationList section */ 610 | }; 611 | rootObject = F78B40EF2255D204000BE07B /* Project object */; 612 | } 613 | -------------------------------------------------------------------------------- /YXClickTextViewDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // YXClickTextViewDemo 4 | // 5 | // Created by yunxin bai on 2019/4/4. 6 | // Copyright © 2019 yunxin bai. 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 | -------------------------------------------------------------------------------- /YXClickTextViewDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // YXClickTextViewDemo 4 | // 5 | // Created by yunxin bai on 2019/4/4. 6 | // Copyright © 2019 yunxin bai. 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 | 24 | - (void)applicationWillResignActive:(UIApplication *)application { 25 | // 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. 26 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 27 | } 28 | 29 | 30 | - (void)applicationDidEnterBackground:(UIApplication *)application { 31 | // 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. 32 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 33 | } 34 | 35 | 36 | - (void)applicationWillEnterForeground:(UIApplication *)application { 37 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 38 | } 39 | 40 | 41 | - (void)applicationDidBecomeActive:(UIApplication *)application { 42 | // 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. 43 | } 44 | 45 | 46 | - (void)applicationWillTerminate:(UIApplication *)application { 47 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 48 | } 49 | 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /YXClickTextViewDemo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /YXClickTextViewDemo/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /YXClickTextViewDemo/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 | -------------------------------------------------------------------------------- /YXClickTextViewDemo/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 | -------------------------------------------------------------------------------- /YXClickTextViewDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /YXClickTextViewDemo/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // YXClickTextViewDemo 4 | // 5 | // Created by yunxin bai on 2019/4/4. 6 | // Copyright © 2019 yunxin bai. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /YXClickTextViewDemo/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // YXClickTextViewDemo 4 | // 5 | // Created by yunxin bai on 2019/4/4. 6 | // Copyright © 2019 yunxin bai. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "YXClickTextView/YXClickTextView.h" 11 | 12 | @interface ViewController () 13 | 14 | @end 15 | 16 | @implementation ViewController 17 | 18 | - (void)viewDidLoad { 19 | [super viewDidLoad]; 20 | // Do any additional setup after loading the view, typically from a nib. 21 | 22 | YXClickTextView *textView1 = [[YXClickTextView alloc] init]; 23 | [self.view addSubview:textView1]; 24 | textView1.textColor = [UIColor blackColor]; 25 | textView1.font = [UIFont systemFontOfSize:14]; 26 | textView1.textAlignment = NSTextAlignmentLeft; 27 | textView1.highlightedBackgroundColor = [UIColor orangeColor]; 28 | 29 | textView1.autoWidth = YES; 30 | textView1.autoHeight = YES; // Default is 'YES' 31 | 32 | textView1.frame = CGRectMake(20, 140, 300, 30); 33 | textView1.text = @"您已阅读并同意《注册与服务协议》"; 34 | textView1.yxkit_assist.attributedSubstring(@"《注册与服务协议》", [UIColor blueColor]); 35 | [textView1 addClickTexts:@[@"《注册与服务协议》",] callback:^(NSString *text, NSRange range) { 36 | NSString *msg = [NSString stringWithFormat:@"text:%@,range:%@",text,NSStringFromRange(range)]; 37 | NSLog(@"%@",msg); 38 | }]; 39 | 40 | 41 | 42 | YXClickTextView *textView2 = [[YXClickTextView alloc] init]; 43 | [self.view addSubview:textView2]; 44 | textView2.textColor = [UIColor blackColor]; 45 | textView2.backgroundColor = [UIColor grayColor]; 46 | textView2.font = [UIFont systemFontOfSize:14]; 47 | textView2.textAlignment = NSTextAlignmentLeft; 48 | textView2.highlightedBackgroundColor = [UIColor orangeColor]; 49 | 50 | textView2.autoWidth = YES; 51 | textView2.autoHeight = YES; // Default is 'YES' 52 | 53 | textView2.frame = CGRectMake(20, 200, 300, 200); 54 | textView2.text = @"在调用工厂类的工厂方法时,由于工厂方法是静态方法,使用起来很方便,可通过类名直接调用,而且只需要传入一个简单的参数即可,在实际开发中,还可以在调用时将所传入的参数保存在XML等格式的配置文件中,修改参数时无须修改任何源代码。"; 55 | textView2.yxkit_assist.attributedSubstring(@"在实际开发中", [UIColor orangeColor]); 56 | [textView2 addClickTexts:@[@"在实际开发中",] callback:^(NSString *text, NSRange range) { 57 | NSString *msg = [NSString stringWithFormat:@"text:%@,range:%@",text,NSStringFromRange(range)]; 58 | NSLog(@"%@",msg); 59 | }]; 60 | } 61 | 62 | 63 | @end 64 | -------------------------------------------------------------------------------- /YXClickTextViewDemo/YXClickTextView/YXClickTextView.h: -------------------------------------------------------------------------------- 1 | // 2 | // YXClickTextView.h 3 | // Forst 4 | // 5 | // Created by yunxin bai on 2018/6/26. 6 | // Copyright © 2018 yunxin bai. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "YXKitAssist.h" 11 | 12 | @class YXClickTextView; 13 | 14 | @protocol YXClickTextViewDelegate 15 | 16 | @optional 17 | - (void)clickTextView:(YXClickTextView *)clickTextView didClickedText:(NSString *)text range:(NSRange)range; 18 | 19 | @end 20 | 21 | typedef void(^YXClickTextBlock)(NSString *text, NSRange range); 22 | 23 | @interface YXClickTextView : UITextView 24 | 25 | @property (nonatomic, weak) id yxDelegate; 26 | 27 | @property (nonatomic, assign) BOOL autoHeight; 28 | 29 | @property (nonatomic, assign) BOOL autoWidth; 30 | 31 | @property (nonatomic, strong) UIColor *highlightedBackgroundColor; 32 | 33 | - (void)addClickTexts:(NSArray *)texts callback:(YXClickTextBlock)callback; 34 | 35 | - (void)addClickTexts:(NSArray *)texts; 36 | 37 | - (void)removeClickText:(NSArray *)texts; 38 | 39 | - (void)removeAllClickText; 40 | 41 | @end 42 | 43 | -------------------------------------------------------------------------------- /YXClickTextViewDemo/YXClickTextView/YXClickTextView.m: -------------------------------------------------------------------------------- 1 | // 2 | // YXClickTextView.m 3 | // Forst 4 | // 5 | // Created by yunxin bai on 2018/6/26. 6 | // Copyright © 2018 yunxin bai. All rights reserved. 7 | // 8 | 9 | #import "YXClickTextView.h" 10 | 11 | @interface YXClickTextModel : NSObject 12 | 13 | @property (nonatomic, copy) NSString *text; 14 | @property (nonatomic, assign) NSRange range; 15 | @property (nonatomic, copy) YXClickTextBlock textBlock; 16 | @property (nonatomic, strong) UIColor *color; 17 | @property (nonatomic, strong) UIFont *font; 18 | 19 | @end 20 | 21 | @implementation YXClickTextModel 22 | @end 23 | 24 | 25 | @interface YXClickTextView () 26 | 27 | @property (nonatomic, strong) NSMutableArray *textArrayM; 28 | @property (nonatomic, strong) YXClickTextModel *model; 29 | 30 | @end 31 | 32 | @implementation YXClickTextView 33 | 34 | - (instancetype)initWithFrame:(CGRect)frame { 35 | self = [super initWithFrame:frame]; 36 | if (self) { 37 | self.editable = NO; 38 | self.scrollEnabled = NO; 39 | self.textContainerInset = UIEdgeInsetsZero; 40 | self.showsVerticalScrollIndicator = NO; 41 | self.showsHorizontalScrollIndicator = NO; 42 | self.textContainer.lineFragmentPadding = 0; 43 | self.selectable = NO; 44 | 45 | _autoHeight = YES; 46 | _autoWidth = NO; 47 | } 48 | return self; 49 | } 50 | 51 | #pragma mark - override 52 | - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { 53 | NSUInteger location = [self characterIndexAtLocation:[touches.anyObject locationInView:self]]; 54 | if (location == NSNotFound) { 55 | return [super touchesBegan:touches withEvent:event]; 56 | } 57 | } 58 | 59 | - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { 60 | NSUInteger location = [self characterIndexAtLocation:[touches.anyObject locationInView:self]]; 61 | if (location == NSNotFound) { 62 | return [super touchesBegan:touches withEvent:event]; 63 | } 64 | } 65 | 66 | - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { 67 | [self shouldCallBack]; 68 | } 69 | 70 | - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { 71 | [self shouldCallBack]; 72 | } 73 | 74 | - (void)willMoveToSuperview:(UIView *)newSuperview { 75 | [super willMoveToSuperview:newSuperview]; 76 | if (newSuperview) { 77 | UIView *hoderView = [newSuperview viewWithTag:10086]; 78 | if (!hoderView) { 79 | hoderView = [[UIView alloc] init]; 80 | hoderView.tag = 10086; 81 | [newSuperview addSubview:hoderView]; 82 | } 83 | } 84 | } 85 | 86 | #pragma mark - private 87 | - (NSUInteger)characterIndexAtLocation:(CGPoint)point { 88 | NSUInteger index = [self.layoutManager glyphIndexForPoint:point inTextContainer:self.textContainer]; 89 | CGRect rect = [self.layoutManager boundingRectForGlyphRange:NSMakeRange(index, 1) inTextContainer:self.textContainer]; 90 | 91 | YXClickTextModel *selectModel = nil; 92 | if (CGRectContainsPoint(rect, point)) { 93 | for (YXClickTextModel *model in self.textArrayM) { 94 | if (index >= model.range.location && index < model.range.location + model.range.length) { 95 | selectModel = model; 96 | break; 97 | } 98 | } 99 | } 100 | if (selectModel) { 101 | self.model = selectModel; 102 | }else { 103 | self.model = nil; 104 | } 105 | if (_highlightedBackgroundColor) { 106 | [self changeClickTextBackgroundColor:self.backgroundColor type:0]; 107 | } 108 | 109 | return NSNotFound; 110 | } 111 | 112 | - (void)shouldCallBack { 113 | if (_model.textBlock) { 114 | _model.textBlock(_model.text, _model.range); 115 | } 116 | if (_model && _yxDelegate && [_yxDelegate respondsToSelector:@selector(clickTextView:didClickedText:range:)]) { 117 | [_yxDelegate clickTextView:self didClickedText:_model.text range:_model.range]; 118 | } 119 | _model = nil; 120 | } 121 | 122 | - (void)allRangeOfText:(NSString *)text inText:(NSString *)theText callBack:(YXClickTextBlock)callback { 123 | NSRange range = [theText rangeOfString:text]; 124 | while (range.location != NSNotFound) { 125 | YXClickTextModel *model = [YXClickTextModel new]; 126 | model.text = text; 127 | model.range = range; 128 | model.textBlock = callback; 129 | [self.textArrayM addObject:model]; 130 | NSUInteger length = range.location + range.length; 131 | range = [theText rangeOfString:text options:kNilOptions range:NSMakeRange(length, theText.length - length)]; 132 | } 133 | } 134 | 135 | - (void)changeClickTextBackgroundColor:(UIColor *)bgcolor type:(NSInteger)type { 136 | if (bgcolor == nil) { 137 | bgcolor = [UIColor clearColor]; 138 | } 139 | if (_model) { 140 | NSMutableAttributedString *mastring = [[NSMutableAttributedString alloc] initWithAttributedString:self.attributedText?:[[NSAttributedString alloc] initWithString:self.text]]; 141 | if (type == 0) { 142 | [mastring addAttribute:NSBackgroundColorAttributeName value:bgcolor range:_model.range]; 143 | }else if (type == 1) { 144 | [mastring removeAttribute:NSBackgroundColorAttributeName range:_model.range]; 145 | } 146 | self.attributedText = mastring; 147 | } 148 | } 149 | 150 | #pragma mark - public method 151 | - (void)addClickTexts:(NSArray *)texts callback:(YXClickTextBlock)callback { 152 | NSString *theText = self.text; 153 | dispatch_async(dispatch_get_global_queue(0, 0), ^{ 154 | if (texts && callback) { 155 | for (NSString *text in texts) { 156 | if (text.length == 0) { 157 | continue; 158 | } 159 | [self allRangeOfText:text inText:theText callBack:callback]; 160 | } 161 | } 162 | }); 163 | } 164 | 165 | - (void)addClickTexts:(NSArray *)texts { 166 | [self addClickTexts:texts callback:nil]; 167 | } 168 | 169 | - (void)removeClickText:(NSArray *)texts { 170 | NSMutableArray *marr = @[].mutableCopy; 171 | for (NSString *text in texts) { 172 | if (text.length == 0) { 173 | continue; 174 | } 175 | for (YXClickTextModel *model in _textArrayM) { 176 | if ([model.text isEqualToString:text]) { 177 | [marr addObject:model]; 178 | } 179 | } 180 | } 181 | [self.textArrayM removeObjectsInArray:marr]; 182 | } 183 | 184 | - (void)removeAllClickText { 185 | [self.textArrayM removeAllObjects]; 186 | } 187 | 188 | #pragma mark - setters and getters 189 | - (NSMutableArray *)textArrayM { 190 | if (!_textArrayM) { 191 | _textArrayM = [NSMutableArray array]; 192 | } 193 | return _textArrayM; 194 | } 195 | 196 | - (void)setText:(NSString *)text { 197 | [super setText:text]; 198 | if (_autoWidth && _autoHeight) { 199 | [self sizeToFit]; 200 | }else if (_autoHeight) { 201 | CGSize size = [self sizeThatFits:self.bounds.size]; 202 | CGRect frame = self.frame; 203 | frame.size.height = size.height; 204 | self.frame = frame; 205 | }else if (_autoWidth) { 206 | CGSize size = [self sizeThatFits:self.bounds.size]; 207 | CGRect frame = self.frame; 208 | frame.size.width = size.width; 209 | self.frame = frame; 210 | } 211 | } 212 | @end 213 | -------------------------------------------------------------------------------- /YXClickTextViewDemo/YXKitAssist/YXKitAssist.h: -------------------------------------------------------------------------------- 1 | // 2 | // YXKitAssist.h 3 | // YXKit 4 | // 5 | // Created by yunxin bai on 2017/8/18. 6 | // Copyright © 2017 yunxin bai. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface YXKitAssist : NSObject 13 | 14 | #pragma mark - UIView 15 | 16 | /** NSNumber */ 17 | - (YXKitAssist*(^)(id))tag; 18 | /** NSValue */ 19 | - (YXKitAssist*(^)(id))frame; 20 | /** NSNumber */ 21 | - (YXKitAssist*(^)(id))alpha; 22 | /** UIColor,NSString(eg.#FFFEEE,0xFFFEEE,0XFFFEEE) */ 23 | - (YXKitAssist*(^)(id))bgColor; 24 | /** UIColor,NSString(eg.#FFFEEE,0xFFFEEE,0XFFFEEE) */ 25 | - (YXKitAssist*(^)(id))bdColor; 26 | /** NSNumber */ 27 | - (YXKitAssist*(^)(id))bdWidth; 28 | /** NSNumber */ 29 | - (YXKitAssist*(^)(id))cnRadius; 30 | /** @(YES) or @(NO) */ 31 | - (YXKitAssist*(^)(id))mtBounds; 32 | 33 | #pragma mark - UILabel & UITextView & UITextField 34 | 35 | /** NSString */ 36 | - (YXKitAssist*(^)(id))text; 37 | /** UIFont */ 38 | - (YXKitAssist*(^)(id))font; 39 | /** UIColor,NSString(eg.#FFFEEE,0xFFFEEE,0XFFFEEE) */ 40 | - (YXKitAssist*(^)(id))color; 41 | /** NSNumber: @0,@1,@2 */ 42 | - (YXKitAssist*(^)(id))align; 43 | /** substring: NSString, value:color or font. */ 44 | - (YXKitAssist*(^)(id,id))attributedSubstring; 45 | /** substring: NSString, value:color or font, range: NSValue. */ 46 | - (YXKitAssist*(^)(id,id,id))attributedSubstringInRange; 47 | 48 | #pragma mark - UILabel 49 | 50 | /** NSNumber(NSInteger) */ 51 | - (YXKitAssist*(^)(id))lines; 52 | /** adjustsFontSizeToFitWidth: @(YES) or @(NO) */ 53 | - (YXKitAssist*(^)(id))adjust; 54 | /** NSNumber(CGFloat) */ 55 | - (YXKitAssist*(^)(id))lineSpace; 56 | /** NSNumber(CGFloat), 0 means no max width limit. */ 57 | - (YXKitAssist*(^)(id))autoWidth; 58 | /** NSNumber(CGFloat), 0 means no max height limit. */ 59 | - (YXKitAssist*(^)(id))autoHeight; 60 | 61 | #pragma mark - UIButton 62 | 63 | /** title: NSString, state: NSNumber */ 64 | - (YXKitAssist*(^)(id,id))titleForState; 65 | /** color: UIColor,NSString(eg.#FFFEEE,0xFFFEEE,0XFFFEEE) , state: NSNumber */ 66 | - (YXKitAssist*(^)(id,id))colorForState; 67 | /** image: UIImage, state: NSNumber */ 68 | - (YXKitAssist*(^)(id,id))imageForState; 69 | /** bgImage: UIImage, state: NSNumber */ 70 | - (YXKitAssist*(^)(id,id))bgImageForState; 71 | /** lineSpace: NSNumber, state: NSNumber */ 72 | - (YXKitAssist*(^)(id,id))lineSpaceForState; 73 | /** space: NSNumber */ 74 | - (YXKitAssist*(^)(id))imageUpTitleDown; 75 | /** space: NSNumber */ 76 | - (YXKitAssist*(^)(id))imageDownTitleUp; 77 | /** space: NSNumber */ 78 | - (YXKitAssist*(^)(id))imageRightTitleLeft; 79 | /** space: NSNumber */ 80 | - (YXKitAssist*(^)(id))imageLeftTitleRight; 81 | /** all center */ 82 | - (YXKitAssist*(^)(void))imageCenterTitleCenter; 83 | /** substring: NSString, value:color or font, state: NSNumber */ 84 | - (YXKitAssist*(^)(id,id,id))attributedSubstringForState; 85 | /** substring: NSString, value:color or font, range: NSValue, state: NSNumber */ 86 | - (YXKitAssist*(^)(id,id,id,id))attributedSubstringInRangeForState; 87 | 88 | #pragma mark - UIImageView 89 | 90 | /** image: UIImage, NSString */ 91 | - (YXKitAssist*(^)(id))image; 92 | /** image: UIImage,NSString, color: UIColor */ 93 | - (YXKitAssist*(^)(id,id))imageForTintColor; 94 | 95 | 96 | #pragma mark - UITextField 97 | 98 | /** borderStyle, NSNumber: @1,@2,@3,@4 */ 99 | - (YXKitAssist*(^)(id))bdStyle; 100 | /** placeholder, NSString */ 101 | - (YXKitAssist*(^)(id))pHolder; 102 | /** placeholder color, UIColor,NSString(eg.#FFFEEE,0xFFFEEE,0XFFFEEE) */ 103 | - (YXKitAssist*(^)(id))pHColor; 104 | /** placeholder font, UIFont */ 105 | - (YXKitAssist*(^)(id))pHFont; 106 | /** clearButtonMode, NSNumber: @1,@2,@3,@4 */ 107 | - (YXKitAssist*(^)(id))cbMode; 108 | /** leftViewMode, NSNumber: @1,@2,@3,@4 */ 109 | - (YXKitAssist*(^)(id))lvMode; 110 | /** rightViewMode, NSNumber: @1,@2,@3,@4 */ 111 | - (YXKitAssist*(^)(id))rvMode; 112 | /** leftView, UIView */ 113 | - (YXKitAssist*(^)(id))lfView; 114 | /** rightView, UIView */ 115 | - (YXKitAssist*(^)(id))rtView; 116 | /** secureTextEntry, BOOL: @YES, @NO */ 117 | - (YXKitAssist*(^)(id))secure; 118 | @end 119 | 120 | @interface UIView (YXKitAssist) 121 | 122 | @property (nonatomic, strong) YXKitAssist *yxkit_assist; 123 | 124 | @end 125 | 126 | @interface UIColor (YXKitAssist) 127 | 128 | + (UIColor *)kitAssist:(id)color; 129 | 130 | @end 131 | -------------------------------------------------------------------------------- /YXClickTextViewDemo/YXKitAssist/YXKitAssist.m: -------------------------------------------------------------------------------- 1 | // 2 | // YXKitAssist.m 3 | // YXKit 4 | // 5 | // Created by yunxin bai on 2017/8/18. 6 | // Copyright © 2017 yunxin bai. All rights reserved. 7 | // 8 | 9 | #import "YXKitAssist.h" 10 | #import 11 | 12 | #define YXKitAssist_imp(sel,imp) \ 13 | - (YXKitAssist*(^)(id))sel{ \ 14 | return ^id(id sel){ \ 15 | UIView *view = self.view; \ 16 | do { \ 17 | imp; \ 18 | } while (0); \ 19 | return view.yxkit_assist; \ 20 | }; \ 21 | } 22 | 23 | 24 | @interface YXKitAssist () 25 | 26 | @property (nonatomic, weak) UIView *view; 27 | 28 | @end 29 | 30 | @implementation YXKitAssist 31 | 32 | #pragma mark - UIView 33 | 34 | YXKitAssist_imp(tag, ({ 35 | if ([tag isKindOfClass:[NSNumber class]]) { 36 | view.tag = [tag integerValue]; 37 | } 38 | })) 39 | 40 | YXKitAssist_imp(frame, ({ 41 | if ([frame isKindOfClass:[NSValue class]]) { 42 | view.frame = [frame CGRectValue]; 43 | } 44 | })) 45 | 46 | YXKitAssist_imp(alpha, ({ 47 | if ([alpha isKindOfClass:[NSNumber class]]) { 48 | view.alpha = [alpha floatValue]; 49 | } 50 | })) 51 | 52 | YXKitAssist_imp(bgColor, ({ 53 | view.backgroundColor = [UIColor kitAssist:bgColor]; 54 | })) 55 | 56 | YXKitAssist_imp(bdColor, ({ 57 | view.layer.borderColor = [[UIColor kitAssist:bdColor] CGColor]; 58 | })) 59 | 60 | YXKitAssist_imp(bdWidth, ({ 61 | if([bdWidth isKindOfClass:[NSNumber class]]){ 62 | view.layer.borderWidth = [bdWidth floatValue]; 63 | } 64 | })) 65 | 66 | YXKitAssist_imp(cnRadius, ({ 67 | if([cnRadius isKindOfClass:[NSNumber class]]){ 68 | view.layer.cornerRadius = [cnRadius floatValue]; 69 | } 70 | })) 71 | 72 | YXKitAssist_imp(mtBounds, ({ 73 | if([mtBounds isKindOfClass:[NSNumber class]]){ 74 | view.layer.masksToBounds = [mtBounds boolValue]; 75 | } 76 | })) 77 | 78 | #pragma mark - UILabel & UITextView & UITextField 79 | 80 | 81 | YXKitAssist_imp(text, ({ 82 | if([text isKindOfClass:[NSString class]]){ 83 | if ([view respondsToSelector:@selector(setText:)]) { 84 | [view performSelector:@selector(setText:) withObject:text]; 85 | } 86 | } 87 | })) 88 | 89 | YXKitAssist_imp(font, ({ 90 | if ([font isKindOfClass:[UIFont class]]) { 91 | if ([view isKindOfClass:[UIButton class]]) { 92 | UIButton *button = (UIButton *)view; 93 | button.titleLabel.font = font; 94 | }else if ([view respondsToSelector:@selector(setFont:)]) { 95 | [view performSelector:@selector(setFont:) withObject:font]; 96 | } 97 | } 98 | })) 99 | 100 | YXKitAssist_imp(color, ({ 101 | if([view respondsToSelector:@selector(setTextColor:)]){ 102 | [view performSelector:@selector(setTextColor:) withObject:[UIColor kitAssist:color]]; 103 | } 104 | })) 105 | 106 | YXKitAssist_imp(align, ({ 107 | if([align isKindOfClass:[NSNumber class]]){ 108 | if([view respondsToSelector:@selector(setTextAlignment:)]){ 109 | [view performSelector:@selector(setTextAlignment:) withObject:align]; 110 | } 111 | } 112 | })) 113 | 114 | YXKitAssist_imp(lines, ({ 115 | if([view isKindOfClass:[UILabel class]] && 116 | [lines isKindOfClass:[NSNumber class]]){ 117 | [(UILabel *)view setNumberOfLines:[lines integerValue]]; 118 | } 119 | })) 120 | 121 | YXKitAssist_imp(adjust, ({ 122 | if([view isKindOfClass:[UILabel class]] && 123 | [adjust isKindOfClass:[NSNumber class]]){ 124 | [(UILabel *)view setNumberOfLines:[adjust boolValue]]; 125 | } 126 | })) 127 | 128 | YXKitAssist_imp(lineSpace, ({ 129 | if([view isKindOfClass:[UILabel class]] && 130 | [lineSpace isKindOfClass:[NSNumber class]]){ 131 | UILabel *label = (UILabel *)view; 132 | CGFloat space = [lineSpace floatValue]; 133 | 134 | NSMutableAttributedString *attStr = [[NSMutableAttributedString alloc] initWithAttributedString:label.attributedText?:[[NSAttributedString alloc] initWithString:label.text]]; 135 | NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init]; 136 | [paragraphStyle setLineSpacing:space < 0 ? 0 : space]; 137 | paragraphStyle.alignment = label.textAlignment; 138 | [attStr addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange(0, [label.text length])]; 139 | label.attributedText = attStr; 140 | } 141 | })) 142 | 143 | YXKitAssist_imp(autoWidth, ({ 144 | if([view isKindOfClass:[UILabel class]]){ 145 | 146 | UILabel *label = (UILabel *)view; 147 | 148 | CGRect frame = label.frame; 149 | label.numberOfLines = 1; 150 | [label sizeToFit]; 151 | 152 | if ([autoWidth floatValue] > 0 && 153 | label.frame.size.width > [autoWidth floatValue]) 154 | { 155 | frame.size.width = [autoWidth floatValue]; 156 | }else{ 157 | frame.size.width = label.frame.size.width; 158 | } 159 | label.frame = frame; 160 | } 161 | })) 162 | 163 | YXKitAssist_imp(autoHeight, ({ 164 | if([view isKindOfClass:[UILabel class]]){ 165 | 166 | UILabel *label = (UILabel *)view; 167 | 168 | CGRect frame = label.frame; 169 | label.numberOfLines = 0; 170 | [label sizeToFit]; 171 | 172 | if ([autoHeight floatValue] > 0 && 173 | label.frame.size.height > [autoHeight floatValue]) { 174 | frame.size.height = [autoHeight floatValue]; 175 | }else{ 176 | frame.size.height = label.frame.size.height; 177 | } 178 | label.frame = frame; 179 | } 180 | })) 181 | 182 | - (YXKitAssist*(^)(id,id))attributedSubstring{ 183 | return ^id(id string, id value){ 184 | UIView *view = self.view; 185 | 186 | if ([view respondsToSelector:@selector(attributedText)]) { 187 | 188 | NSString *text = [view performSelector:@selector(text)]; 189 | NSAttributedString *attributedText = [view performSelector:@selector(attributedText)]; 190 | if (!attributedText) { 191 | attributedText = [[NSAttributedString alloc] initWithString:text]; 192 | } 193 | 194 | // color 195 | if ([value isKindOfClass:[UIColor class]]) { 196 | NSMutableAttributedString *attStr = [[NSMutableAttributedString alloc] initWithAttributedString:attributedText]; 197 | [attStr addAttribute:NSForegroundColorAttributeName value:value range:[text rangeOfString:string]]; 198 | [view performSelector:@selector(setAttributedText:) withObject:attStr]; 199 | } 200 | // font 201 | else if ([value isKindOfClass:[UIFont class]]){ 202 | NSMutableAttributedString *attStr = [[NSMutableAttributedString alloc] initWithAttributedString:attributedText]; 203 | [attStr addAttribute:NSFontAttributeName value:value range:[text rangeOfString:string]]; 204 | [view performSelector:@selector(setAttributedText:) withObject:attStr]; 205 | } 206 | } 207 | 208 | return view.yxkit_assist; 209 | }; 210 | } 211 | 212 | 213 | - (YXKitAssist*(^)(id,id,id))attributedSubstringInRange{ 214 | return ^id(id string, id value, id range){ 215 | UIView *view = self.view; 216 | 217 | if ([view respondsToSelector:@selector(attributedText)]) { 218 | 219 | NSString *text = [view performSelector:@selector(text)]; 220 | NSAttributedString *attributedText = [view performSelector:@selector(attributedText)]; 221 | if (!attributedText) { 222 | attributedText = [[NSAttributedString alloc] initWithString:text]; 223 | } 224 | 225 | // color 226 | if ([value isKindOfClass:[UIColor class]] && 227 | [range isKindOfClass:[NSValue class]]) { 228 | NSMutableAttributedString *attStr = [[NSMutableAttributedString alloc] initWithAttributedString:attributedText]; 229 | [attStr addAttribute:NSForegroundColorAttributeName value:value range:[range rangeValue]]; 230 | [view performSelector:@selector(setAttributedText:) withObject:attStr]; 231 | } 232 | // font 233 | else if ([value isKindOfClass:[UIFont class]] && 234 | [range isKindOfClass:[NSValue class]]){ 235 | NSMutableAttributedString *attStr = [[NSMutableAttributedString alloc] initWithAttributedString:attributedText]; 236 | [attStr addAttribute:NSFontAttributeName value:value range:[range rangeValue]]; 237 | [view performSelector:@selector(setAttributedText:) withObject:attStr]; 238 | } 239 | } 240 | 241 | return view.yxkit_assist; 242 | }; 243 | } 244 | 245 | 246 | 247 | #pragma mark - UIButton 248 | 249 | - (YXKitAssist*(^)(id,id))titleForState{ 250 | return ^id(id title, id state){ 251 | UIView *view = self.view; 252 | 253 | if ([view isKindOfClass:[UIButton class]] && 254 | [title isKindOfClass:[NSString class]] && 255 | [state isKindOfClass:[NSNumber class]]) { 256 | [(UIButton *)view setTitle:title forState:[state integerValue]]; 257 | } 258 | 259 | return view.yxkit_assist; 260 | }; 261 | } 262 | 263 | - (YXKitAssist*(^)(id,id))colorForState{ 264 | return ^id(id color, id state){ 265 | UIView *view = self.view; 266 | 267 | if ([view isKindOfClass:[UIButton class]]&& 268 | [state isKindOfClass:[NSNumber class]]) { 269 | [(UIButton *)view setTitleColor:[UIColor kitAssist:color] forState:[state integerValue]]; 270 | } 271 | 272 | return view.yxkit_assist; 273 | }; 274 | } 275 | 276 | - (YXKitAssist*(^)(id,id))imageForState{ 277 | return ^id(id image, id state){ 278 | UIView *view = self.view; 279 | 280 | if ([view isKindOfClass:[UIButton class]] && 281 | [image isKindOfClass:[NSString class]] && 282 | [state isKindOfClass:[NSNumber class]]) { 283 | [(UIButton *)view setImage:image forState:[state integerValue]]; 284 | } 285 | 286 | return view.yxkit_assist; 287 | }; 288 | } 289 | 290 | - (YXKitAssist*(^)(id,id))bgImageForState{ 291 | return ^id(id bgImage, id state){ 292 | UIView *view = self.view; 293 | 294 | if ([view isKindOfClass:[UIButton class]] && 295 | [bgImage isKindOfClass:[NSString class]] && 296 | [state isKindOfClass:[NSNumber class]]) { 297 | [(UIButton *)view setBackgroundImage:bgImage forState:[state integerValue]]; 298 | } 299 | 300 | return view.yxkit_assist; 301 | }; 302 | } 303 | 304 | - (YXKitAssist*(^)(id,id))lineSpaceForState{ 305 | return ^id(id lineSpace, id state){ 306 | UIView *view = self.view; 307 | 308 | if ([view isKindOfClass:[UIButton class]]) { 309 | UIButton *button = (UIButton *)view; 310 | CGFloat space = [lineSpace integerValue]; 311 | 312 | NSAttributedString *attributedString = [button attributedTitleForState:[state integerValue]]?:[[NSAttributedString alloc] initWithString:button.currentTitle]; 313 | NSMutableAttributedString *attStr = [[NSMutableAttributedString alloc] initWithAttributedString:attributedString]; 314 | NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init]; 315 | [paragraphStyle setLineSpacing:space < 0 ? 0 : space]; 316 | paragraphStyle.alignment = button.titleLabel.textAlignment; 317 | [attStr addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange(0, [button.currentAttributedTitle.string length])]; 318 | [button setAttributedTitle:attStr forState:[state integerValue]]; 319 | } 320 | 321 | return view.yxkit_assist; 322 | }; 323 | } 324 | 325 | - (YXKitAssist*(^)(id))imageUpTitleDown{ 326 | return ^id(id offsetY){ 327 | UIView *view = self.view; 328 | 329 | if ([view isKindOfClass:[UIButton class]]) { 330 | UIButton *button = (UIButton *)view; 331 | CGFloat Y = [offsetY floatValue]; 332 | 333 | [button layoutSubviews]; 334 | [button setImageEdgeInsets:UIEdgeInsetsMake(-button.titleLabel.intrinsicContentSize.height-Y,0,0,-button.titleLabel.intrinsicContentSize.width)]; 335 | [button setTitleEdgeInsets:UIEdgeInsetsMake(0,-CGRectGetWidth(button.imageView.frame),-CGRectGetHeight(button.imageView.frame)-Y,0)]; 336 | 337 | } 338 | 339 | return view.yxkit_assist; 340 | }; 341 | } 342 | 343 | - (YXKitAssist*(^)(id))imageDownTitleUp{ 344 | return ^id(id offsetY){ 345 | UIView *view = self.view; 346 | 347 | if ([view isKindOfClass:[UIButton class]]) { 348 | UIButton *button = (UIButton *)view; 349 | CGFloat Y = [offsetY floatValue]; 350 | 351 | [button layoutSubviews]; 352 | [button setImageEdgeInsets:UIEdgeInsetsMake(button.titleLabel.intrinsicContentSize.height+Y,0,0,-button.titleLabel.intrinsicContentSize.width)]; 353 | [button setTitleEdgeInsets:UIEdgeInsetsMake(-CGRectGetWidth(button.imageView.frame)-Y,-CGRectGetWidth(button.imageView.frame),0,0)]; 354 | 355 | } 356 | 357 | return view.yxkit_assist; 358 | }; 359 | } 360 | 361 | - (YXKitAssist*(^)(id))imageRightTitleLeft{ 362 | return ^id(id offsetX){ 363 | UIView *view = self.view; 364 | 365 | if ([view isKindOfClass:[UIButton class]]) { 366 | UIButton *button = (UIButton *)view; 367 | CGFloat X = [offsetX floatValue]; 368 | 369 | [button layoutSubviews]; 370 | [button setImageEdgeInsets:UIEdgeInsetsMake(0,0,0,-button.titleLabel.intrinsicContentSize.width*2-X)]; 371 | [button setTitleEdgeInsets:UIEdgeInsetsMake(0,-button.imageView.frame.size.width*2-X,0,0)]; 372 | 373 | } 374 | 375 | return view.yxkit_assist; 376 | }; 377 | } 378 | 379 | - (YXKitAssist*(^)(id))imageLeftTitleRight{ 380 | return ^id(id offsetX){ 381 | UIView *view = self.view; 382 | 383 | if ([view isKindOfClass:[UIButton class]]) { 384 | UIButton *button = (UIButton *)view; 385 | CGFloat X = [offsetX floatValue]; 386 | 387 | [button layoutSubviews]; 388 | [button setImageEdgeInsets:UIEdgeInsetsMake(0,0,0,X)]; 389 | [button setTitleEdgeInsets:UIEdgeInsetsMake(0,X,0,0)]; 390 | 391 | } 392 | 393 | return view.yxkit_assist; 394 | }; 395 | } 396 | 397 | - (YXKitAssist*(^)(void))imageCenterTitleCenter{ 398 | return ^id(){ 399 | UIView *view = self.view; 400 | 401 | if ([view isKindOfClass:[UIButton class]]) { 402 | UIButton *button = (UIButton *)view; 403 | 404 | [button layoutSubviews]; 405 | [button setImageEdgeInsets:UIEdgeInsetsMake(0,0,0,-button.titleLabel.intrinsicContentSize.width)]; 406 | [button setTitleEdgeInsets:UIEdgeInsetsMake(0,-button.imageView.frame.size.width,0,0)]; 407 | 408 | } 409 | 410 | return view.yxkit_assist; 411 | }; 412 | } 413 | 414 | - (YXKitAssist*(^)(id,id,id))attributedSubstringForState{ 415 | return ^id(id substring, id value, id state){ 416 | UIView *view = self.view; 417 | 418 | if ([view isKindOfClass:[UIButton class]]) { 419 | UIButton *button = (UIButton *)view; 420 | 421 | // color 422 | if ([value isKindOfClass:[UIColor class]]) { 423 | NSAttributedString *attributedString = [button attributedTitleForState:[state integerValue]]?:[[NSAttributedString alloc] initWithString:button.currentTitle]; 424 | NSMutableAttributedString *attStr = [[NSMutableAttributedString alloc] initWithAttributedString:attributedString]; 425 | [attStr addAttribute:NSForegroundColorAttributeName value:value range:[attributedString.string rangeOfString:substring]]; 426 | [button setAttributedTitle:attStr forState:[state integerValue]]; 427 | } 428 | // font 429 | else if ([value isKindOfClass:[UIFont class]]){ 430 | NSAttributedString *attributedString = [button attributedTitleForState:[state integerValue]]?:[[NSAttributedString alloc] initWithString:button.currentTitle]; 431 | NSMutableAttributedString *attStr = [[NSMutableAttributedString alloc] initWithAttributedString:attributedString]; 432 | [attStr addAttribute:NSFontAttributeName value:value range:[attributedString.string rangeOfString:substring]]; 433 | [button setAttributedTitle:attStr forState:[state integerValue]]; 434 | } 435 | } 436 | 437 | return view.yxkit_assist; 438 | }; 439 | } 440 | 441 | - (YXKitAssist*(^)(id,id,id,id))attributedSubstringInRangeForState{ 442 | return ^id(id substring, id value, id range, id state){ 443 | UIView *view = self.view; 444 | 445 | if ([view isKindOfClass:[UIButton class]]) { 446 | UIButton *button = (UIButton *)view; 447 | 448 | // color 449 | if ([value isKindOfClass:[UIColor class]] && 450 | [range isKindOfClass:[NSValue class]]) { 451 | NSAttributedString *attributedString = [button attributedTitleForState:[state integerValue]]?:[[NSAttributedString alloc] initWithString:button.currentTitle]; 452 | NSMutableAttributedString *attStr = [[NSMutableAttributedString alloc] initWithAttributedString:attributedString]; 453 | [attStr addAttribute:NSForegroundColorAttributeName value:value range:[range rangeValue]]; 454 | [button setAttributedTitle:attStr forState:[state integerValue]]; 455 | } 456 | // font 457 | else if ([value isKindOfClass:[UIFont class]] && 458 | [range isKindOfClass:[NSValue class]]){ 459 | NSAttributedString *attributedString = [button attributedTitleForState:[state integerValue]]?:[[NSAttributedString alloc] initWithString:button.currentTitle]; 460 | NSMutableAttributedString *attStr = [[NSMutableAttributedString alloc] initWithAttributedString:attributedString]; 461 | [attStr addAttribute:NSFontAttributeName value:value range:[range rangeValue]]; 462 | [button setAttributedTitle:attStr forState:[state integerValue]]; 463 | } 464 | } 465 | 466 | return view.yxkit_assist; 467 | }; 468 | } 469 | 470 | #pragma mark - UIImageView 471 | 472 | YXKitAssist_imp(image, ({ 473 | if ([view isKindOfClass:[UIImageView class]]) { 474 | if ([image isKindOfClass:[NSString class]]) { 475 | [view performSelector:@selector(setImage:) withObject:[UIImage imageNamed:image]]; 476 | }else if ([image isKindOfClass:[UIImage class]]){ 477 | [view performSelector:@selector(setImage:) withObject:image]; 478 | } 479 | } 480 | })) 481 | 482 | - (YXKitAssist*(^)(id,id))imageForTintColor{ 483 | return ^id(id image, id color){ 484 | UIView *view = self.view; 485 | 486 | if ([view isKindOfClass:[UIImageView class]]) { 487 | UIImageView *imageView = (UIImageView *)view; 488 | imageView.tintColor = [UIColor kitAssist:color]; 489 | 490 | if ([image isKindOfClass:[NSString class]]) { 491 | imageView.image = [[UIImage imageNamed:image] 492 | imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; 493 | }else if ([image isKindOfClass:[UIImage class]]){ 494 | imageView.image = [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; 495 | } 496 | } 497 | 498 | return view.yxkit_assist; 499 | }; 500 | } 501 | 502 | 503 | #pragma mark - UITextField 504 | 505 | YXKitAssist_imp(bdStyle, ({ 506 | if ([view isKindOfClass:[UITextField class]] && 507 | [bdStyle isKindOfClass:[NSNumber class]]) { 508 | [view performSelector:@selector(setBorderStyle:) withObject:bdStyle]; 509 | } 510 | })) 511 | 512 | YXKitAssist_imp(pHolder, ({ 513 | if ([view isKindOfClass:[UITextField class]] && 514 | [pHolder isKindOfClass:[NSString class]]) { 515 | [view performSelector:@selector(setPlaceholder:) withObject:pHolder]; 516 | } 517 | })) 518 | 519 | YXKitAssist_imp(pHColor, ({ 520 | if ([view isKindOfClass:[UITextField class]]) { 521 | [view setValue:[UIColor kitAssist:pHColor] forKeyPath:@"_placeholderLabel.textColor"]; 522 | } 523 | })) 524 | 525 | YXKitAssist_imp(pHFont, ({ 526 | if ([view isKindOfClass:[UITextField class]] && 527 | [pHFont isKindOfClass:[UIFont class]]) { 528 | [self setValue:pHFont forKeyPath:@"_placeholderLabel.font"]; 529 | } 530 | })) 531 | 532 | YXKitAssist_imp(cbMode, ({ 533 | if ([view isKindOfClass:[UITextField class]] && 534 | [cbMode isKindOfClass:[NSNumber class]]) { 535 | [view performSelector:@selector(setClearButtonMode:) withObject:cbMode]; 536 | } 537 | })) 538 | 539 | YXKitAssist_imp(lvMode, ({ 540 | if ([view isKindOfClass:[UITextField class]] && 541 | [lvMode isKindOfClass:[NSNumber class]]) { 542 | [view performSelector:@selector(setLeftViewMode:) withObject:lvMode]; 543 | } 544 | })) 545 | 546 | YXKitAssist_imp(rvMode, ({ 547 | if ([view isKindOfClass:[UITextField class]] && 548 | [rvMode isKindOfClass:[NSNumber class]]) { 549 | [view performSelector:@selector(setRightViewMode:) withObject:rvMode]; 550 | } 551 | })) 552 | 553 | YXKitAssist_imp(lfView, ({ 554 | if ([view isKindOfClass:[UITextField class]] && 555 | [lfView isKindOfClass:[UIView class]]) { 556 | [view performSelector:@selector(setLeftView:) withObject:lfView]; 557 | } 558 | })) 559 | 560 | YXKitAssist_imp(rtView, ({ 561 | if ([view isKindOfClass:[UITextField class]] && 562 | [rtView isKindOfClass:[UIView class]]) { 563 | [view performSelector:@selector(setRightView:) withObject:rtView]; 564 | } 565 | })) 566 | 567 | YXKitAssist_imp(secure, ({ 568 | if ([view isKindOfClass:[UITextField class]] && 569 | [secure isKindOfClass:[NSNumber class]]) { 570 | [view performSelector:@selector(setSecureTextEntry:) withObject:secure]; 571 | } 572 | })) 573 | 574 | @end 575 | 576 | @implementation UIView (YXKitAssist) 577 | 578 | - (void)setYxkit_assist:(YXKitAssist *)yxkit_assist { 579 | objc_setAssociatedObject(self, @selector(yxkit_assist), yxkit_assist, OBJC_ASSOCIATION_RETAIN); 580 | } 581 | 582 | - (YXKitAssist *)yxkit_assist { 583 | YXKitAssist *kitAssist = objc_getAssociatedObject(self, _cmd); 584 | if (!kitAssist) { 585 | kitAssist = [[YXKitAssist alloc] init]; 586 | [kitAssist setValue:self forKey:@"view"]; 587 | self.yxkit_assist = kitAssist; 588 | } 589 | return kitAssist; 590 | } 591 | 592 | @end 593 | 594 | @implementation UIColor (YXKitAssist) 595 | + (UIColor *)kitAssist:(id)color{ 596 | if ([color isKindOfClass:[NSString class]]){ 597 | return [self colorWithString:color]; 598 | } 599 | else if ([color isKindOfClass:[UIColor class]]){ 600 | return color; 601 | } 602 | return [UIColor blackColor]; 603 | } 604 | 605 | + (UIColor *)colorWithString:(NSString *)string{ 606 | NSString *xxStr = [[string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] uppercaseString]; 607 | 608 | // length is 6 or 8 609 | if ([xxStr length] < 6){ 610 | return [UIColor blackColor]; 611 | } 612 | 613 | // 0x 614 | if ([xxStr hasPrefix:@"0x"] || [xxStr hasPrefix:@"0X"]){ 615 | xxStr = [xxStr substringFromIndex:2]; 616 | } 617 | 618 | // # 619 | if ([xxStr hasPrefix:@"#"]){ 620 | xxStr = [xxStr substringFromIndex:1]; 621 | } 622 | 623 | if ([xxStr length] != 6){ 624 | return [UIColor blackColor]; 625 | } 626 | 627 | // r, g, b 628 | NSRange range; 629 | range.location = 0; 630 | range.length = 2; 631 | // r 632 | NSString *rStr = [xxStr substringWithRange:range]; 633 | // g 634 | range.location = 2; 635 | NSString *gStr = [xxStr substringWithRange:range]; 636 | // b 637 | range.location = 4; 638 | NSString *bStr = [xxStr substringWithRange:range]; 639 | 640 | // 641 | unsigned int r, g, b; 642 | [[NSScanner scannerWithString:rStr] scanHexInt:&r]; 643 | [[NSScanner scannerWithString:gStr] scanHexInt:&g]; 644 | [[NSScanner scannerWithString:bStr] scanHexInt:&b]; 645 | 646 | UIColor *xxColor = [UIColor colorWithRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:1]; 647 | 648 | return xxColor; 649 | } 650 | 651 | 652 | @end 653 | -------------------------------------------------------------------------------- /YXClickTextViewDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // YXClickTextViewDemo 4 | // 5 | // Created by yunxin bai on 2019/4/4. 6 | // Copyright © 2019 yunxin bai. 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 | -------------------------------------------------------------------------------- /YXClickTextViewDemoTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /YXClickTextViewDemoTests/YXClickTextViewDemoTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // YXClickTextViewDemoTests.m 3 | // YXClickTextViewDemoTests 4 | // 5 | // Created by yunxin bai on 2019/4/4. 6 | // Copyright © 2019 yunxin bai. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface YXClickTextViewDemoTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation YXClickTextViewDemoTests 16 | 17 | - (void)setUp { 18 | // Put setup code here. This method is called before the invocation of each test method in the class. 19 | } 20 | 21 | - (void)tearDown { 22 | // Put teardown code here. This method is called after the invocation of each test method in the class. 23 | } 24 | 25 | - (void)testExample { 26 | // This is an example of a functional test case. 27 | // Use XCTAssert and related functions to verify your tests produce the correct results. 28 | } 29 | 30 | - (void)testPerformanceExample { 31 | // This is an example of a performance test case. 32 | [self measureBlock:^{ 33 | // Put the code you want to measure the time of here. 34 | }]; 35 | } 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /YXClickTextViewDemoUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /YXClickTextViewDemoUITests/YXClickTextViewDemoUITests.m: -------------------------------------------------------------------------------- 1 | // 2 | // YXClickTextViewDemoUITests.m 3 | // YXClickTextViewDemoUITests 4 | // 5 | // Created by yunxin bai on 2019/4/4. 6 | // Copyright © 2019 yunxin bai. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface YXClickTextViewDemoUITests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation YXClickTextViewDemoUITests 16 | 17 | - (void)setUp { 18 | // Put setup code here. This method is called before the invocation of each test method in the class. 19 | 20 | // In UI tests it is usually best to stop immediately when a failure occurs. 21 | self.continueAfterFailure = NO; 22 | 23 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 24 | [[[XCUIApplication alloc] init] launch]; 25 | 26 | // 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. 27 | } 28 | 29 | - (void)tearDown { 30 | // Put teardown code here. This method is called after the invocation of each test method in the class. 31 | } 32 | 33 | - (void)testExample { 34 | // Use recording to get started writing UI tests. 35 | // Use XCTAssert and related functions to verify your tests produce the correct results. 36 | } 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /YXKitAssist/YXKitAssist.h: -------------------------------------------------------------------------------- 1 | // 2 | // YXKitAssist.h 3 | // YXKit 4 | // 5 | // Created by yunxin bai on 2017/8/18. 6 | // Copyright © 2017 yunxin bai. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface YXKitAssist : NSObject 13 | 14 | #pragma mark - UIView 15 | 16 | /** NSNumber */ 17 | - (YXKitAssist*(^)(id))tag; 18 | /** NSValue */ 19 | - (YXKitAssist*(^)(id))frame; 20 | /** NSNumber */ 21 | - (YXKitAssist*(^)(id))alpha; 22 | /** UIColor,NSString(eg.#FFFEEE,0xFFFEEE,0XFFFEEE) */ 23 | - (YXKitAssist*(^)(id))bgColor; 24 | /** UIColor,NSString(eg.#FFFEEE,0xFFFEEE,0XFFFEEE) */ 25 | - (YXKitAssist*(^)(id))bdColor; 26 | /** NSNumber */ 27 | - (YXKitAssist*(^)(id))bdWidth; 28 | /** NSNumber */ 29 | - (YXKitAssist*(^)(id))cnRadius; 30 | /** @(YES) or @(NO) */ 31 | - (YXKitAssist*(^)(id))mtBounds; 32 | 33 | #pragma mark - UILabel & UITextView & UITextField 34 | 35 | /** NSString */ 36 | - (YXKitAssist*(^)(id))text; 37 | /** UIFont */ 38 | - (YXKitAssist*(^)(id))font; 39 | /** UIColor,NSString(eg.#FFFEEE,0xFFFEEE,0XFFFEEE) */ 40 | - (YXKitAssist*(^)(id))color; 41 | /** NSNumber: @0,@1,@2 */ 42 | - (YXKitAssist*(^)(id))align; 43 | /** substring: NSString, value:color or font. */ 44 | - (YXKitAssist*(^)(id,id))attributedSubstring; 45 | /** substring: NSString, value:color or font, range: NSValue. */ 46 | - (YXKitAssist*(^)(id,id,id))attributedSubstringInRange; 47 | 48 | #pragma mark - UILabel 49 | 50 | /** NSNumber(NSInteger) */ 51 | - (YXKitAssist*(^)(id))lines; 52 | /** adjustsFontSizeToFitWidth: @(YES) or @(NO) */ 53 | - (YXKitAssist*(^)(id))adjust; 54 | /** NSNumber(CGFloat) */ 55 | - (YXKitAssist*(^)(id))lineSpace; 56 | /** NSNumber(CGFloat), 0 means no max width limit. */ 57 | - (YXKitAssist*(^)(id))autoWidth; 58 | /** NSNumber(CGFloat), 0 means no max height limit. */ 59 | - (YXKitAssist*(^)(id))autoHeight; 60 | 61 | #pragma mark - UIButton 62 | 63 | /** title: NSString, state: NSNumber */ 64 | - (YXKitAssist*(^)(id,id))titleForState; 65 | /** color: UIColor,NSString(eg.#FFFEEE,0xFFFEEE,0XFFFEEE) , state: NSNumber */ 66 | - (YXKitAssist*(^)(id,id))colorForState; 67 | /** image: UIImage, state: NSNumber */ 68 | - (YXKitAssist*(^)(id,id))imageForState; 69 | /** bgImage: UIImage, state: NSNumber */ 70 | - (YXKitAssist*(^)(id,id))bgImageForState; 71 | /** lineSpace: NSNumber, state: NSNumber */ 72 | - (YXKitAssist*(^)(id,id))lineSpaceForState; 73 | /** space: NSNumber */ 74 | - (YXKitAssist*(^)(id))imageUpTitleDown; 75 | /** space: NSNumber */ 76 | - (YXKitAssist*(^)(id))imageDownTitleUp; 77 | /** space: NSNumber */ 78 | - (YXKitAssist*(^)(id))imageRightTitleLeft; 79 | /** space: NSNumber */ 80 | - (YXKitAssist*(^)(id))imageLeftTitleRight; 81 | /** all center */ 82 | - (YXKitAssist*(^)(void))imageCenterTitleCenter; 83 | /** substring: NSString, value:color or font, state: NSNumber */ 84 | - (YXKitAssist*(^)(id,id,id))attributedSubstringForState; 85 | /** substring: NSString, value:color or font, range: NSValue, state: NSNumber */ 86 | - (YXKitAssist*(^)(id,id,id,id))attributedSubstringInRangeForState; 87 | 88 | #pragma mark - UIImageView 89 | 90 | /** image: UIImage, NSString */ 91 | - (YXKitAssist*(^)(id))image; 92 | /** image: UIImage,NSString, color: UIColor */ 93 | - (YXKitAssist*(^)(id,id))imageForTintColor; 94 | 95 | 96 | #pragma mark - UITextField 97 | 98 | /** borderStyle, NSNumber: @1,@2,@3,@4 */ 99 | - (YXKitAssist*(^)(id))bdStyle; 100 | /** placeholder, NSString */ 101 | - (YXKitAssist*(^)(id))pHolder; 102 | /** placeholder color, UIColor,NSString(eg.#FFFEEE,0xFFFEEE,0XFFFEEE) */ 103 | - (YXKitAssist*(^)(id))pHColor; 104 | /** placeholder font, UIFont */ 105 | - (YXKitAssist*(^)(id))pHFont; 106 | /** clearButtonMode, NSNumber: @1,@2,@3,@4 */ 107 | - (YXKitAssist*(^)(id))cbMode; 108 | /** leftViewMode, NSNumber: @1,@2,@3,@4 */ 109 | - (YXKitAssist*(^)(id))lvMode; 110 | /** rightViewMode, NSNumber: @1,@2,@3,@4 */ 111 | - (YXKitAssist*(^)(id))rvMode; 112 | /** leftView, UIView */ 113 | - (YXKitAssist*(^)(id))lfView; 114 | /** rightView, UIView */ 115 | - (YXKitAssist*(^)(id))rtView; 116 | /** secureTextEntry, BOOL: @YES, @NO */ 117 | - (YXKitAssist*(^)(id))secure; 118 | @end 119 | 120 | @interface UIView (YXKitAssist) 121 | 122 | @property (nonatomic, strong) YXKitAssist *yxkit_assist; 123 | 124 | @end 125 | 126 | @interface UIColor (YXKitAssist) 127 | 128 | + (UIColor *)kitAssist:(id)color; 129 | 130 | @end 131 | -------------------------------------------------------------------------------- /YXKitAssist/YXKitAssist.m: -------------------------------------------------------------------------------- 1 | // 2 | // YXKitAssist.m 3 | // YXKit 4 | // 5 | // Created by yunxin bai on 2017/8/18. 6 | // Copyright © 2017 yunxin bai. All rights reserved. 7 | // 8 | 9 | #import "YXKitAssist.h" 10 | #import 11 | 12 | #define YXKitAssist_imp(sel,imp) \ 13 | - (YXKitAssist*(^)(id))sel{ \ 14 | return ^id(id sel){ \ 15 | UIView *view = self.view; \ 16 | do { \ 17 | imp; \ 18 | } while (0); \ 19 | return view.yxkit_assist; \ 20 | }; \ 21 | } 22 | 23 | 24 | @interface YXKitAssist () 25 | 26 | @property (nonatomic, weak) UIView *view; 27 | 28 | @end 29 | 30 | @implementation YXKitAssist 31 | 32 | #pragma mark - UIView 33 | 34 | YXKitAssist_imp(tag, ({ 35 | if ([tag isKindOfClass:[NSNumber class]]) { 36 | view.tag = [tag integerValue]; 37 | } 38 | })) 39 | 40 | YXKitAssist_imp(frame, ({ 41 | if ([frame isKindOfClass:[NSValue class]]) { 42 | view.frame = [frame CGRectValue]; 43 | } 44 | })) 45 | 46 | YXKitAssist_imp(alpha, ({ 47 | if ([alpha isKindOfClass:[NSNumber class]]) { 48 | view.alpha = [alpha floatValue]; 49 | } 50 | })) 51 | 52 | YXKitAssist_imp(bgColor, ({ 53 | view.backgroundColor = [UIColor kitAssist:bgColor]; 54 | })) 55 | 56 | YXKitAssist_imp(bdColor, ({ 57 | view.layer.borderColor = [[UIColor kitAssist:bdColor] CGColor]; 58 | })) 59 | 60 | YXKitAssist_imp(bdWidth, ({ 61 | if([bdWidth isKindOfClass:[NSNumber class]]){ 62 | view.layer.borderWidth = [bdWidth floatValue]; 63 | } 64 | })) 65 | 66 | YXKitAssist_imp(cnRadius, ({ 67 | if([cnRadius isKindOfClass:[NSNumber class]]){ 68 | view.layer.cornerRadius = [cnRadius floatValue]; 69 | } 70 | })) 71 | 72 | YXKitAssist_imp(mtBounds, ({ 73 | if([mtBounds isKindOfClass:[NSNumber class]]){ 74 | view.layer.masksToBounds = [mtBounds boolValue]; 75 | } 76 | })) 77 | 78 | #pragma mark - UILabel & UITextView & UITextField 79 | 80 | 81 | YXKitAssist_imp(text, ({ 82 | if([text isKindOfClass:[NSString class]]){ 83 | if ([view respondsToSelector:@selector(setText:)]) { 84 | [view performSelector:@selector(setText:) withObject:text]; 85 | } 86 | } 87 | })) 88 | 89 | YXKitAssist_imp(font, ({ 90 | if ([font isKindOfClass:[UIFont class]]) { 91 | if ([view isKindOfClass:[UIButton class]]) { 92 | UIButton *button = (UIButton *)view; 93 | button.titleLabel.font = font; 94 | }else if ([view respondsToSelector:@selector(setFont:)]) { 95 | [view performSelector:@selector(setFont:) withObject:font]; 96 | } 97 | } 98 | })) 99 | 100 | YXKitAssist_imp(color, ({ 101 | if([view respondsToSelector:@selector(setTextColor:)]){ 102 | [view performSelector:@selector(setTextColor:) withObject:[UIColor kitAssist:color]]; 103 | } 104 | })) 105 | 106 | YXKitAssist_imp(align, ({ 107 | if([align isKindOfClass:[NSNumber class]]){ 108 | if([view respondsToSelector:@selector(setTextAlignment:)]){ 109 | [view performSelector:@selector(setTextAlignment:) withObject:align]; 110 | } 111 | } 112 | })) 113 | 114 | YXKitAssist_imp(lines, ({ 115 | if([view isKindOfClass:[UILabel class]] && 116 | [lines isKindOfClass:[NSNumber class]]){ 117 | [(UILabel *)view setNumberOfLines:[lines integerValue]]; 118 | } 119 | })) 120 | 121 | YXKitAssist_imp(adjust, ({ 122 | if([view isKindOfClass:[UILabel class]] && 123 | [adjust isKindOfClass:[NSNumber class]]){ 124 | [(UILabel *)view setNumberOfLines:[adjust boolValue]]; 125 | } 126 | })) 127 | 128 | YXKitAssist_imp(lineSpace, ({ 129 | if([view isKindOfClass:[UILabel class]] && 130 | [lineSpace isKindOfClass:[NSNumber class]]){ 131 | UILabel *label = (UILabel *)view; 132 | CGFloat space = [lineSpace floatValue]; 133 | 134 | NSMutableAttributedString *attStr = [[NSMutableAttributedString alloc] initWithAttributedString:label.attributedText?:[[NSAttributedString alloc] initWithString:label.text]]; 135 | NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init]; 136 | [paragraphStyle setLineSpacing:space < 0 ? 0 : space]; 137 | paragraphStyle.alignment = label.textAlignment; 138 | [attStr addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange(0, [label.text length])]; 139 | label.attributedText = attStr; 140 | } 141 | })) 142 | 143 | YXKitAssist_imp(autoWidth, ({ 144 | if([view isKindOfClass:[UILabel class]]){ 145 | 146 | UILabel *label = (UILabel *)view; 147 | 148 | CGRect frame = label.frame; 149 | label.numberOfLines = 1; 150 | [label sizeToFit]; 151 | 152 | if ([autoWidth floatValue] > 0 && 153 | label.frame.size.width > [autoWidth floatValue]) 154 | { 155 | frame.size.width = [autoWidth floatValue]; 156 | }else{ 157 | frame.size.width = label.frame.size.width; 158 | } 159 | label.frame = frame; 160 | } 161 | })) 162 | 163 | YXKitAssist_imp(autoHeight, ({ 164 | if([view isKindOfClass:[UILabel class]]){ 165 | 166 | UILabel *label = (UILabel *)view; 167 | 168 | CGRect frame = label.frame; 169 | label.numberOfLines = 0; 170 | [label sizeToFit]; 171 | 172 | if ([autoHeight floatValue] > 0 && 173 | label.frame.size.height > [autoHeight floatValue]) { 174 | frame.size.height = [autoHeight floatValue]; 175 | }else{ 176 | frame.size.height = label.frame.size.height; 177 | } 178 | label.frame = frame; 179 | } 180 | })) 181 | 182 | - (YXKitAssist*(^)(id,id))attributedSubstring{ 183 | return ^id(id string, id value){ 184 | UIView *view = self.view; 185 | 186 | if ([view respondsToSelector:@selector(attributedText)]) { 187 | 188 | NSString *text = [view performSelector:@selector(text)]; 189 | NSAttributedString *attributedText = [view performSelector:@selector(attributedText)]; 190 | if (!attributedText) { 191 | attributedText = [[NSAttributedString alloc] initWithString:text]; 192 | } 193 | 194 | // color 195 | if ([value isKindOfClass:[UIColor class]]) { 196 | NSMutableAttributedString *attStr = [[NSMutableAttributedString alloc] initWithAttributedString:attributedText]; 197 | [attStr addAttribute:NSForegroundColorAttributeName value:value range:[text rangeOfString:string]]; 198 | [view performSelector:@selector(setAttributedText:) withObject:attStr]; 199 | } 200 | // font 201 | else if ([value isKindOfClass:[UIFont class]]){ 202 | NSMutableAttributedString *attStr = [[NSMutableAttributedString alloc] initWithAttributedString:attributedText]; 203 | [attStr addAttribute:NSFontAttributeName value:value range:[text rangeOfString:string]]; 204 | [view performSelector:@selector(setAttributedText:) withObject:attStr]; 205 | } 206 | } 207 | 208 | return view.yxkit_assist; 209 | }; 210 | } 211 | 212 | 213 | - (YXKitAssist*(^)(id,id,id))attributedSubstringInRange{ 214 | return ^id(id string, id value, id range){ 215 | UIView *view = self.view; 216 | 217 | if ([view respondsToSelector:@selector(attributedText)]) { 218 | 219 | NSString *text = [view performSelector:@selector(text)]; 220 | NSAttributedString *attributedText = [view performSelector:@selector(attributedText)]; 221 | if (!attributedText) { 222 | attributedText = [[NSAttributedString alloc] initWithString:text]; 223 | } 224 | 225 | // color 226 | if ([value isKindOfClass:[UIColor class]] && 227 | [range isKindOfClass:[NSValue class]]) { 228 | NSMutableAttributedString *attStr = [[NSMutableAttributedString alloc] initWithAttributedString:attributedText]; 229 | [attStr addAttribute:NSForegroundColorAttributeName value:value range:[range rangeValue]]; 230 | [view performSelector:@selector(setAttributedText:) withObject:attStr]; 231 | } 232 | // font 233 | else if ([value isKindOfClass:[UIFont class]] && 234 | [range isKindOfClass:[NSValue class]]){ 235 | NSMutableAttributedString *attStr = [[NSMutableAttributedString alloc] initWithAttributedString:attributedText]; 236 | [attStr addAttribute:NSFontAttributeName value:value range:[range rangeValue]]; 237 | [view performSelector:@selector(setAttributedText:) withObject:attStr]; 238 | } 239 | } 240 | 241 | return view.yxkit_assist; 242 | }; 243 | } 244 | 245 | 246 | 247 | #pragma mark - UIButton 248 | 249 | - (YXKitAssist*(^)(id,id))titleForState{ 250 | return ^id(id title, id state){ 251 | UIView *view = self.view; 252 | 253 | if ([view isKindOfClass:[UIButton class]] && 254 | [title isKindOfClass:[NSString class]] && 255 | [state isKindOfClass:[NSNumber class]]) { 256 | [(UIButton *)view setTitle:title forState:[state integerValue]]; 257 | } 258 | 259 | return view.yxkit_assist; 260 | }; 261 | } 262 | 263 | - (YXKitAssist*(^)(id,id))colorForState{ 264 | return ^id(id color, id state){ 265 | UIView *view = self.view; 266 | 267 | if ([view isKindOfClass:[UIButton class]]&& 268 | [state isKindOfClass:[NSNumber class]]) { 269 | [(UIButton *)view setTitleColor:[UIColor kitAssist:color] forState:[state integerValue]]; 270 | } 271 | 272 | return view.yxkit_assist; 273 | }; 274 | } 275 | 276 | - (YXKitAssist*(^)(id,id))imageForState{ 277 | return ^id(id image, id state){ 278 | UIView *view = self.view; 279 | 280 | if ([view isKindOfClass:[UIButton class]] && 281 | [image isKindOfClass:[NSString class]] && 282 | [state isKindOfClass:[NSNumber class]]) { 283 | [(UIButton *)view setImage:image forState:[state integerValue]]; 284 | } 285 | 286 | return view.yxkit_assist; 287 | }; 288 | } 289 | 290 | - (YXKitAssist*(^)(id,id))bgImageForState{ 291 | return ^id(id bgImage, id state){ 292 | UIView *view = self.view; 293 | 294 | if ([view isKindOfClass:[UIButton class]] && 295 | [bgImage isKindOfClass:[NSString class]] && 296 | [state isKindOfClass:[NSNumber class]]) { 297 | [(UIButton *)view setBackgroundImage:bgImage forState:[state integerValue]]; 298 | } 299 | 300 | return view.yxkit_assist; 301 | }; 302 | } 303 | 304 | - (YXKitAssist*(^)(id,id))lineSpaceForState{ 305 | return ^id(id lineSpace, id state){ 306 | UIView *view = self.view; 307 | 308 | if ([view isKindOfClass:[UIButton class]]) { 309 | UIButton *button = (UIButton *)view; 310 | CGFloat space = [lineSpace integerValue]; 311 | 312 | NSAttributedString *attributedString = [button attributedTitleForState:[state integerValue]]?:[[NSAttributedString alloc] initWithString:button.currentTitle]; 313 | NSMutableAttributedString *attStr = [[NSMutableAttributedString alloc] initWithAttributedString:attributedString]; 314 | NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init]; 315 | [paragraphStyle setLineSpacing:space < 0 ? 0 : space]; 316 | paragraphStyle.alignment = button.titleLabel.textAlignment; 317 | [attStr addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange(0, [button.currentAttributedTitle.string length])]; 318 | [button setAttributedTitle:attStr forState:[state integerValue]]; 319 | } 320 | 321 | return view.yxkit_assist; 322 | }; 323 | } 324 | 325 | - (YXKitAssist*(^)(id))imageUpTitleDown{ 326 | return ^id(id offsetY){ 327 | UIView *view = self.view; 328 | 329 | if ([view isKindOfClass:[UIButton class]]) { 330 | UIButton *button = (UIButton *)view; 331 | CGFloat Y = [offsetY floatValue]; 332 | 333 | [button layoutSubviews]; 334 | [button setImageEdgeInsets:UIEdgeInsetsMake(-button.titleLabel.intrinsicContentSize.height-Y,0,0,-button.titleLabel.intrinsicContentSize.width)]; 335 | [button setTitleEdgeInsets:UIEdgeInsetsMake(0,-CGRectGetWidth(button.imageView.frame),-CGRectGetHeight(button.imageView.frame)-Y,0)]; 336 | 337 | } 338 | 339 | return view.yxkit_assist; 340 | }; 341 | } 342 | 343 | - (YXKitAssist*(^)(id))imageDownTitleUp{ 344 | return ^id(id offsetY){ 345 | UIView *view = self.view; 346 | 347 | if ([view isKindOfClass:[UIButton class]]) { 348 | UIButton *button = (UIButton *)view; 349 | CGFloat Y = [offsetY floatValue]; 350 | 351 | [button layoutSubviews]; 352 | [button setImageEdgeInsets:UIEdgeInsetsMake(button.titleLabel.intrinsicContentSize.height+Y,0,0,-button.titleLabel.intrinsicContentSize.width)]; 353 | [button setTitleEdgeInsets:UIEdgeInsetsMake(-CGRectGetWidth(button.imageView.frame)-Y,-CGRectGetWidth(button.imageView.frame),0,0)]; 354 | 355 | } 356 | 357 | return view.yxkit_assist; 358 | }; 359 | } 360 | 361 | - (YXKitAssist*(^)(id))imageRightTitleLeft{ 362 | return ^id(id offsetX){ 363 | UIView *view = self.view; 364 | 365 | if ([view isKindOfClass:[UIButton class]]) { 366 | UIButton *button = (UIButton *)view; 367 | CGFloat X = [offsetX floatValue]; 368 | 369 | [button layoutSubviews]; 370 | [button setImageEdgeInsets:UIEdgeInsetsMake(0,0,0,-button.titleLabel.intrinsicContentSize.width*2-X)]; 371 | [button setTitleEdgeInsets:UIEdgeInsetsMake(0,-button.imageView.frame.size.width*2-X,0,0)]; 372 | 373 | } 374 | 375 | return view.yxkit_assist; 376 | }; 377 | } 378 | 379 | - (YXKitAssist*(^)(id))imageLeftTitleRight{ 380 | return ^id(id offsetX){ 381 | UIView *view = self.view; 382 | 383 | if ([view isKindOfClass:[UIButton class]]) { 384 | UIButton *button = (UIButton *)view; 385 | CGFloat X = [offsetX floatValue]; 386 | 387 | [button layoutSubviews]; 388 | [button setImageEdgeInsets:UIEdgeInsetsMake(0,0,0,X)]; 389 | [button setTitleEdgeInsets:UIEdgeInsetsMake(0,X,0,0)]; 390 | 391 | } 392 | 393 | return view.yxkit_assist; 394 | }; 395 | } 396 | 397 | - (YXKitAssist*(^)(void))imageCenterTitleCenter{ 398 | return ^id(){ 399 | UIView *view = self.view; 400 | 401 | if ([view isKindOfClass:[UIButton class]]) { 402 | UIButton *button = (UIButton *)view; 403 | 404 | [button layoutSubviews]; 405 | [button setImageEdgeInsets:UIEdgeInsetsMake(0,0,0,-button.titleLabel.intrinsicContentSize.width)]; 406 | [button setTitleEdgeInsets:UIEdgeInsetsMake(0,-button.imageView.frame.size.width,0,0)]; 407 | 408 | } 409 | 410 | return view.yxkit_assist; 411 | }; 412 | } 413 | 414 | - (YXKitAssist*(^)(id,id,id))attributedSubstringForState{ 415 | return ^id(id substring, id value, id state){ 416 | UIView *view = self.view; 417 | 418 | if ([view isKindOfClass:[UIButton class]]) { 419 | UIButton *button = (UIButton *)view; 420 | 421 | // color 422 | if ([value isKindOfClass:[UIColor class]]) { 423 | NSAttributedString *attributedString = [button attributedTitleForState:[state integerValue]]?:[[NSAttributedString alloc] initWithString:button.currentTitle]; 424 | NSMutableAttributedString *attStr = [[NSMutableAttributedString alloc] initWithAttributedString:attributedString]; 425 | [attStr addAttribute:NSForegroundColorAttributeName value:value range:[attributedString.string rangeOfString:substring]]; 426 | [button setAttributedTitle:attStr forState:[state integerValue]]; 427 | } 428 | // font 429 | else if ([value isKindOfClass:[UIFont class]]){ 430 | NSAttributedString *attributedString = [button attributedTitleForState:[state integerValue]]?:[[NSAttributedString alloc] initWithString:button.currentTitle]; 431 | NSMutableAttributedString *attStr = [[NSMutableAttributedString alloc] initWithAttributedString:attributedString]; 432 | [attStr addAttribute:NSFontAttributeName value:value range:[attributedString.string rangeOfString:substring]]; 433 | [button setAttributedTitle:attStr forState:[state integerValue]]; 434 | } 435 | } 436 | 437 | return view.yxkit_assist; 438 | }; 439 | } 440 | 441 | - (YXKitAssist*(^)(id,id,id,id))attributedSubstringInRangeForState{ 442 | return ^id(id substring, id value, id range, id state){ 443 | UIView *view = self.view; 444 | 445 | if ([view isKindOfClass:[UIButton class]]) { 446 | UIButton *button = (UIButton *)view; 447 | 448 | // color 449 | if ([value isKindOfClass:[UIColor class]] && 450 | [range isKindOfClass:[NSValue class]]) { 451 | NSAttributedString *attributedString = [button attributedTitleForState:[state integerValue]]?:[[NSAttributedString alloc] initWithString:button.currentTitle]; 452 | NSMutableAttributedString *attStr = [[NSMutableAttributedString alloc] initWithAttributedString:attributedString]; 453 | [attStr addAttribute:NSForegroundColorAttributeName value:value range:[range rangeValue]]; 454 | [button setAttributedTitle:attStr forState:[state integerValue]]; 455 | } 456 | // font 457 | else if ([value isKindOfClass:[UIFont class]] && 458 | [range isKindOfClass:[NSValue class]]){ 459 | NSAttributedString *attributedString = [button attributedTitleForState:[state integerValue]]?:[[NSAttributedString alloc] initWithString:button.currentTitle]; 460 | NSMutableAttributedString *attStr = [[NSMutableAttributedString alloc] initWithAttributedString:attributedString]; 461 | [attStr addAttribute:NSFontAttributeName value:value range:[range rangeValue]]; 462 | [button setAttributedTitle:attStr forState:[state integerValue]]; 463 | } 464 | } 465 | 466 | return view.yxkit_assist; 467 | }; 468 | } 469 | 470 | #pragma mark - UIImageView 471 | 472 | YXKitAssist_imp(image, ({ 473 | if ([view isKindOfClass:[UIImageView class]]) { 474 | if ([image isKindOfClass:[NSString class]]) { 475 | [view performSelector:@selector(setImage:) withObject:[UIImage imageNamed:image]]; 476 | }else if ([image isKindOfClass:[UIImage class]]){ 477 | [view performSelector:@selector(setImage:) withObject:image]; 478 | } 479 | } 480 | })) 481 | 482 | - (YXKitAssist*(^)(id,id))imageForTintColor{ 483 | return ^id(id image, id color){ 484 | UIView *view = self.view; 485 | 486 | if ([view isKindOfClass:[UIImageView class]]) { 487 | UIImageView *imageView = (UIImageView *)view; 488 | imageView.tintColor = [UIColor kitAssist:color]; 489 | 490 | if ([image isKindOfClass:[NSString class]]) { 491 | imageView.image = [[UIImage imageNamed:image] 492 | imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; 493 | }else if ([image isKindOfClass:[UIImage class]]){ 494 | imageView.image = [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; 495 | } 496 | } 497 | 498 | return view.yxkit_assist; 499 | }; 500 | } 501 | 502 | 503 | #pragma mark - UITextField 504 | 505 | YXKitAssist_imp(bdStyle, ({ 506 | if ([view isKindOfClass:[UITextField class]] && 507 | [bdStyle isKindOfClass:[NSNumber class]]) { 508 | [view performSelector:@selector(setBorderStyle:) withObject:bdStyle]; 509 | } 510 | })) 511 | 512 | YXKitAssist_imp(pHolder, ({ 513 | if ([view isKindOfClass:[UITextField class]] && 514 | [pHolder isKindOfClass:[NSString class]]) { 515 | [view performSelector:@selector(setPlaceholder:) withObject:pHolder]; 516 | } 517 | })) 518 | 519 | YXKitAssist_imp(pHColor, ({ 520 | if ([view isKindOfClass:[UITextField class]]) { 521 | [view setValue:[UIColor kitAssist:pHColor] forKeyPath:@"_placeholderLabel.textColor"]; 522 | } 523 | })) 524 | 525 | YXKitAssist_imp(pHFont, ({ 526 | if ([view isKindOfClass:[UITextField class]] && 527 | [pHFont isKindOfClass:[UIFont class]]) { 528 | [self setValue:pHFont forKeyPath:@"_placeholderLabel.font"]; 529 | } 530 | })) 531 | 532 | YXKitAssist_imp(cbMode, ({ 533 | if ([view isKindOfClass:[UITextField class]] && 534 | [cbMode isKindOfClass:[NSNumber class]]) { 535 | [view performSelector:@selector(setClearButtonMode:) withObject:cbMode]; 536 | } 537 | })) 538 | 539 | YXKitAssist_imp(lvMode, ({ 540 | if ([view isKindOfClass:[UITextField class]] && 541 | [lvMode isKindOfClass:[NSNumber class]]) { 542 | [view performSelector:@selector(setLeftViewMode:) withObject:lvMode]; 543 | } 544 | })) 545 | 546 | YXKitAssist_imp(rvMode, ({ 547 | if ([view isKindOfClass:[UITextField class]] && 548 | [rvMode isKindOfClass:[NSNumber class]]) { 549 | [view performSelector:@selector(setRightViewMode:) withObject:rvMode]; 550 | } 551 | })) 552 | 553 | YXKitAssist_imp(lfView, ({ 554 | if ([view isKindOfClass:[UITextField class]] && 555 | [lfView isKindOfClass:[UIView class]]) { 556 | [view performSelector:@selector(setLeftView:) withObject:lfView]; 557 | } 558 | })) 559 | 560 | YXKitAssist_imp(rtView, ({ 561 | if ([view isKindOfClass:[UITextField class]] && 562 | [rtView isKindOfClass:[UIView class]]) { 563 | [view performSelector:@selector(setRightView:) withObject:rtView]; 564 | } 565 | })) 566 | 567 | YXKitAssist_imp(secure, ({ 568 | if ([view isKindOfClass:[UITextField class]] && 569 | [secure isKindOfClass:[NSNumber class]]) { 570 | [view performSelector:@selector(setSecureTextEntry:) withObject:secure]; 571 | } 572 | })) 573 | 574 | @end 575 | 576 | @implementation UIView (YXKitAssist) 577 | 578 | - (void)setYxkit_assist:(YXKitAssist *)yxkit_assist { 579 | objc_setAssociatedObject(self, @selector(yxkit_assist), yxkit_assist, OBJC_ASSOCIATION_RETAIN); 580 | } 581 | 582 | - (YXKitAssist *)yxkit_assist { 583 | YXKitAssist *kitAssist = objc_getAssociatedObject(self, _cmd); 584 | if (!kitAssist) { 585 | kitAssist = [[YXKitAssist alloc] init]; 586 | [kitAssist setValue:self forKey:@"view"]; 587 | self.yxkit_assist = kitAssist; 588 | } 589 | return kitAssist; 590 | } 591 | 592 | @end 593 | 594 | @implementation UIColor (YXKitAssist) 595 | + (UIColor *)kitAssist:(id)color{ 596 | if ([color isKindOfClass:[NSString class]]){ 597 | return [self colorWithString:color]; 598 | } 599 | else if ([color isKindOfClass:[UIColor class]]){ 600 | return color; 601 | } 602 | return [UIColor blackColor]; 603 | } 604 | 605 | + (UIColor *)colorWithString:(NSString *)string{ 606 | NSString *xxStr = [[string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] uppercaseString]; 607 | 608 | // length is 6 or 8 609 | if ([xxStr length] < 6){ 610 | return [UIColor blackColor]; 611 | } 612 | 613 | // 0x 614 | if ([xxStr hasPrefix:@"0x"] || [xxStr hasPrefix:@"0X"]){ 615 | xxStr = [xxStr substringFromIndex:2]; 616 | } 617 | 618 | // # 619 | if ([xxStr hasPrefix:@"#"]){ 620 | xxStr = [xxStr substringFromIndex:1]; 621 | } 622 | 623 | if ([xxStr length] != 6){ 624 | return [UIColor blackColor]; 625 | } 626 | 627 | // r, g, b 628 | NSRange range; 629 | range.location = 0; 630 | range.length = 2; 631 | // r 632 | NSString *rStr = [xxStr substringWithRange:range]; 633 | // g 634 | range.location = 2; 635 | NSString *gStr = [xxStr substringWithRange:range]; 636 | // b 637 | range.location = 4; 638 | NSString *bStr = [xxStr substringWithRange:range]; 639 | 640 | // 641 | unsigned int r, g, b; 642 | [[NSScanner scannerWithString:rStr] scanHexInt:&r]; 643 | [[NSScanner scannerWithString:gStr] scanHexInt:&g]; 644 | [[NSScanner scannerWithString:bStr] scanHexInt:&b]; 645 | 646 | UIColor *xxColor = [UIColor colorWithRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:1]; 647 | 648 | return xxColor; 649 | } 650 | 651 | 652 | @end 653 | -------------------------------------------------------------------------------- /example.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinexy/YXClickTextView/96c1b15c91228a142aef7d528b0188e08ae66ec2/example.jpg --------------------------------------------------------------------------------