├── CJLabel.podspec ├── CJLabel ├── CJLabel+UIMenu.h ├── CJLabel+UIMenu.m ├── CJLabel.h ├── CJLabel.m ├── CJLabelConfigure.h └── CJLabelConfigure.m ├── Example ├── CJLabelTest.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcuserdata │ │ ├── CK.xcuserdatad │ │ ├── xcdebugger │ │ │ └── Breakpoints_v2.xcbkptlist │ │ └── xcschemes │ │ │ ├── CJLabelTest.xcscheme │ │ │ └── xcschememanagement.plist │ │ └── Y.C.Lian.xcuserdatad │ │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ │ └── xcschemes │ │ ├── CJLabelTest.xcscheme │ │ └── xcschememanagement.plist ├── CJLabelTest │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ ├── CJLabel.imageset │ │ │ ├── CJLabel@2x.png │ │ │ ├── CJLabel@3x.png │ │ │ └── Contents.json │ │ ├── Contents.json │ │ └── backImage.imageset │ │ │ ├── Contents.json │ │ │ ├── backImage@2x.png │ │ │ └── backImage@3x.png │ ├── AttributedTableViewCell.h │ ├── AttributedTableViewCell.m │ ├── AttributedTableViewCell.xib │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── CJLabelTest.entitlements │ ├── Common.h │ ├── Example.json │ ├── FirstDetailViewController.h │ ├── FirstDetailViewController.m │ ├── FirstDetailViewController.xib │ ├── Info.plist │ ├── SecondDetailViewController.h │ ├── SecondDetailViewController.m │ ├── SecondDetailViewController.xib │ ├── ViewController.h │ ├── ViewController.m │ └── main.m ├── CJLabelTestTests │ ├── CJLabelTestTests.m │ └── Info.plist └── CJLabelTestUITests │ ├── CJLabelTestUITests.m │ └── Info.plist ├── LICENSE └── README.md /CJLabel.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod spec lint CJLabel.podspec' to ensure this is a 3 | # valid spec and to remove all comments including this before submitting the spec. 4 | # 5 | # To learn more about Podspec attributes see http://docs.cocoapods.org/specification.html 6 | # To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/ 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | 11 | s.name = "CJLabel" 12 | s.version = "4.7.3" 13 | s.summary = "A drop-in replacement for UILabel that supports NSAttributedString, rich text, links, select copy and more." 14 | s.homepage = "https://github.com/lele8446/CJLabelTest" 15 | # s.license = "MIT" 16 | s.license = { :type => 'Apache License, Version 2.0', :text => <<-LICENSE 17 | Licensed under the Apache License, Version 2.0 (the "License"); 18 | you may not use this file except in compliance with the License. 19 | You may obtain a copy of the License at 20 | 21 | http://www.apache.org/licenses/LICENSE-2.0 22 | 23 | Unless required by applicable law or agreed to in writing, software 24 | distributed under the License is distributed on an "AS IS" BASIS, 25 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 26 | See the License for the specific language governing permissions and 27 | limitations under the License. 28 | LICENSE 29 | } 30 | s.author = { "ChiJinLian" => "lele8446@foxmail.com" } 31 | s.platform = :ios, "7.0" 32 | s.source = { :git => "https://github.com/lele8446/CJLabelTest.git", :tag => "#{s.version}" } 33 | s.source_files = "CJLabel/*" 34 | 35 | end 36 | -------------------------------------------------------------------------------- /CJLabel/CJLabel+UIMenu.h: -------------------------------------------------------------------------------- 1 | // 2 | // CJLabel+UIMenu.h 3 | // CJLabelTest 4 | // 5 | // Created by ChiJinLian on 2021/1/29. 6 | // Copyright © 2021 cjl. All rights reserved. 7 | // 8 | 9 | #import "CJLabel.h" 10 | 11 | @interface CJLabel (UIMenu) 12 | /** 13 | 长按全选文本后弹出的UIMenu菜单(类似微信朋友圈全选复制功能) 14 | */ 15 | @property (nonatomic, strong, readonly) NSMutableArray *menuItems; 16 | /** 17 | 长按全选文本的背景色 18 | */ 19 | @property (nonatomic, strong, readonly) UIColor *selectTextBackColor; 20 | 21 | /// 长按全选文本后弹出自定义UIMenu菜单 22 | /// @param menus 自定义UIMenu菜单 23 | /// @param selectTextBackColor 全选文本背景色 24 | /// @param colorAlpha 背景色透明度 25 | /// @param clickMenuCompletion 点击自定义UIMenu菜单回调 26 | - (void)showSelectAllTextWithMenus:(NSArray *)menus 27 | selectTextBackColor:(UIColor *)selectTextBackColor 28 | colorAlpha:(CGFloat)colorAlpha 29 | clickMenuCompletion:(void (^)(NSString *menuTitle, CJLabel *label))clickMenuCompletion; 30 | @end 31 | -------------------------------------------------------------------------------- /CJLabel/CJLabel+UIMenu.m: -------------------------------------------------------------------------------- 1 | // 2 | // CJLabel+UIMenu.m 3 | // CJLabelTest 4 | // 5 | // Created by ChiJinLian on 2021/1/29. 6 | // Copyright © 2021 cjl. All rights reserved. 7 | // 8 | 9 | #import "CJLabel+UIMenu.h" 10 | #import 11 | 12 | #define UIMenuSELPrefix @"CJLabel_Menu_" 13 | 14 | @interface CJLabel () 15 | @property (nonatomic, strong) UIColor *selectTextBackColor; 16 | @property (nonatomic, strong) NSMutableArray *menuItems; 17 | @property (nonatomic, copy) void(^clickMenuCompletion)(NSString *menuTitle, CJLabel *label); 18 | @end 19 | 20 | @interface SelectMenuForwardingTarget : NSObject 21 | @end 22 | @implementation SelectMenuForwardingTarget 23 | 24 | - (void)forwardingSelector:(NSString *)selectorName label:(CJLabel *)label { 25 | NSString *menuTitle = [selectorName stringByReplacingOccurrencesOfString:UIMenuSELPrefix withString:@""]; 26 | if (label.clickMenuCompletion) { 27 | label.clickMenuCompletion(menuTitle, label); 28 | } 29 | } 30 | + (BOOL)resolveInstanceMethod:(SEL)sel { 31 | NSString *selectorName = NSStringFromSelector(sel); 32 | if ([selectorName hasPrefix:UIMenuSELPrefix]) { 33 | class_addMethod([self class], sel, imp_implementationWithBlock(^(id self, NSString *str) { 34 | // NSLog(@"imp block, sel = %@", selectorName); 35 | }), "v@"); 36 | } 37 | return [super resolveInstanceMethod:sel]; 38 | } 39 | @end 40 | 41 | 42 | @implementation CJLabel (UIMenu) 43 | 44 | static char selectTextBackColorKey; 45 | - (void)setSelectTextBackColor:(UIColor *)selectTextBackColor { 46 | objc_setAssociatedObject(self, &selectTextBackColorKey, selectTextBackColor, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 47 | } 48 | - (UIColor *)selectTextBackColor { 49 | UIColor *color = objc_getAssociatedObject(self, &selectTextBackColorKey); 50 | if (!color) { 51 | color = CJUIRGBColor(0,84,166,0.2); 52 | } 53 | return color; 54 | } 55 | 56 | static char menuItemsKey; 57 | - (void)setMenuItems:(NSMutableArray *)menuItems { 58 | objc_setAssociatedObject(self, &menuItemsKey, menuItems, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 59 | } 60 | - (NSMutableArray *)menuItems { 61 | return objc_getAssociatedObject(self, &menuItemsKey); 62 | } 63 | 64 | static char clickMenuCompletionKey; 65 | - (void)setClickMenuCompletion:(void (^)(NSString *, CJLabel *))clickMenuCompletion { 66 | objc_setAssociatedObject(self, &clickMenuCompletionKey, clickMenuCompletion, OBJC_ASSOCIATION_COPY_NONATOMIC); 67 | } 68 | - (void (^)(NSString *, CJLabel *))clickMenuCompletion { 69 | return objc_getAssociatedObject(self, &clickMenuCompletionKey); 70 | } 71 | 72 | - (void)showSelectAllTextWithMenus:(NSArray *)menus selectTextBackColor:(UIColor *)selectTextBackColor colorAlpha:(CGFloat)colorAlpha clickMenuCompletion:(void (^)(NSString *menuTitle, CJLabel *label))clickMenuCompletion { 73 | self.enableCopy = NO; 74 | self.clickMenuCompletion = clickMenuCompletion; 75 | 76 | if (colorAlpha == 0) { 77 | colorAlpha = 0.2; 78 | } 79 | self.selectTextBackColor = [selectTextBackColor colorWithAlphaComponent:colorAlpha]; 80 | NSMutableArray *menuItems = [NSMutableArray array]; 81 | for (NSString *str in menus) { 82 | NSString *selectorName = [NSString stringWithFormat:@"%@%@",UIMenuSELPrefix,str]; 83 | UIMenuItem *menuItem = [[UIMenuItem alloc] initWithTitle:str action: NSSelectorFromString(selectorName)]; 84 | [menuItems addObject:menuItem]; 85 | } 86 | self.menuItems = menuItems; 87 | } 88 | 89 | #pragma mark - UIResponder 90 | - (BOOL)canBecomeFirstResponder { 91 | return YES; 92 | } 93 | 94 | - (BOOL)canPerformAction:(SEL)action withSender:(id)sender { 95 | // 需要有文字才能支持选择复制 96 | if (self.attributedText || self.text) { 97 | NSString *selectorName = NSStringFromSelector(action); 98 | if ([selectorName hasPrefix:UIMenuSELPrefix]) { 99 | return YES; 100 | } 101 | } 102 | return [super canPerformAction:action withSender:sender]; 103 | } 104 | 105 | - (id)forwardingTargetForSelector:(SEL)aSelector { 106 | NSString *selectorName = NSStringFromSelector(aSelector); 107 | if ([selectorName hasPrefix:UIMenuSELPrefix]) { 108 | SelectMenuForwardingTarget *target = [SelectMenuForwardingTarget new]; 109 | __weak typeof(self)wSelf = self; 110 | [target forwardingSelector:selectorName label:wSelf]; 111 | return target; 112 | } 113 | else{ 114 | return [super forwardingTargetForSelector:aSelector]; 115 | } 116 | } 117 | 118 | - (void)menuItemClick { 119 | [self hideMenuItems]; 120 | } 121 | 122 | - (void)hideMenuItems { 123 | if (self.menuItems.count > 0) { 124 | NSMutableArray *newMenuItems = [NSMutableArray array]; 125 | NSArray *customMenuTitle = [self.menuItems valueForKeyPath:@"title"]; 126 | for (UIMenuItem *menuItem in [UIMenuController sharedMenuController].menuItems) { 127 | if (![customMenuTitle containsObject:menuItem.title]) { 128 | [newMenuItems addObject:menuItem]; 129 | } 130 | } 131 | [UIMenuController sharedMenuController].menuItems = newMenuItems; 132 | } 133 | UIView *allTextSelectBackView = [self viewWithTag:[@"allTextSelectBackView" hash]]; 134 | [allTextSelectBackView removeFromSuperview]; 135 | allTextSelectBackView = nil; 136 | } 137 | @end 138 | -------------------------------------------------------------------------------- /CJLabel/CJLabel.h: -------------------------------------------------------------------------------- 1 | // 2 | // CJLabel.h 3 | // CJLabelTest 4 | // 5 | // Created by ChiJinLian on 17/3/31. 6 | // Copyright © 2017年 ChiJinLian. All rights reserved. 7 | // 8 | /* 9 | ____ _ _ _ _ 10 | / ___| | | | | __ _ | |__ ___ | | 11 | | | _ | | | | / _` | | '_ \ / _ \ | | 12 | | |___ | |_| | | |___ | (_| | | |_) | | __/ | | 13 | \____| \___/ |_____| \__,_| |_.__/ \___| |_| 14 | */ 15 | 16 | #import 17 | #import 18 | #import "CJLabelConfigure.h" 19 | 20 | @class CJLabel; 21 | @class CJLabelLinkModel; 22 | 23 | @protocol CJLabelLinkDelegate 24 | @optional 25 | /** 26 | 点击链点回调 27 | 28 | @param label CJLabel 29 | @param linkModel 链点model 30 | */ 31 | - (void)CJLable:(CJLabel *)label didClickLink:(CJLabelLinkModel *)linkModel; 32 | 33 | /** 34 | 长按点击链点回调 35 | 36 | @param label CJLabel 37 | @param linkModel 链点model 38 | */ 39 | - (void)CJLable:(CJLabel *)label didLongPressLink:(CJLabelLinkModel *)linkModel; 40 | @end 41 | 42 | 43 | /** 44 | * CJLabel 继承自 UILabel,支持富文本展示、图文混排、添加自定义点击链点以及选择复制等功能。 45 | * 46 | * 47 | * CJLabel 与 UILabel 不同点: 48 | * 49 | 1. 禁止使用`-init`初始化!! 50 | 2. `enableCopy` 长按或双击可唤起`UIMenuController`进行选择、全选、复制文本操作 51 | 3. `attributedText` 与 `text` 均可设置富文本 52 | 4. 不支持`NSAttachmentAttributeName`,`NSTextAttachment`!!显示自定义view请调用: 53 | `+ initWithView:viewSize:lineAlignment:configure:`或者 54 | `+ insertViewAtAttrString:view:viewSize:atIndex:lineAlignment:configure:`方法初始化`NSAttributedString`后显示 55 | 5. `extendsLinkTouchArea`设置是否扩大链点点击识别范围 56 | 6. `shadowRadius`设置文本阴影模糊半径 57 | 7. `textInsets` 设置文本内边距 58 | 8. `verticalAlignment` 设置垂直方向的文本对齐方式。 59 | 注意与显示图片时候的`imagelineAlignment`作区分,`self.verticalAlignment`对应的是整体文本在垂直方向的对齐方式,而`imagelineAlignment`只对图片所在行的垂直对齐方式有效 60 | 9. `delegate` 点击链点代理 61 | 10. `kCJBackgroundFillColorAttributeName` 背景填充颜色,属性优先级低于`NSBackgroundColorAttributeName`,如果设置`NSBackgroundColorAttributeName`会忽略`kCJBackgroundFillColorAttributeName`的设置 62 | 11. `kCJBackgroundStrokeColorAttributeName ` 背景边框线颜色 63 | 12. `kCJBackgroundLineWidthAttributeName ` 背景边框线宽度 64 | 13. `kCJBackgroundLineCornerRadiusAttributeName ` 背景边框线圆角弧度 65 | 14. `kCJActiveBackgroundFillColorAttributeName ` 点击时候的背景填充颜色属性优先级同 66 | `kCJBackgroundFillColorAttributeName` 67 | 15. `kCJActiveBackgroundStrokeColorAttributeName ` 点击时候的背景边框线颜色 68 | 16. 支持添加自定义样式、可点击(长按)的文本点击链点 69 | * 70 | * 71 | * CJLabel 已知bug: 72 | * 73 | `numberOfLines`大于0且小于实际`label.numberOfLines`,同时`verticalAlignment`不等于`CJContentVerticalAlignmentTop`时: 74 | 文本显示位置有偏差 75 | * 76 | */ 77 | @interface CJLabel : UILabel 78 | 79 | /** 80 | * 指定初始化函数为 -initWithFrame: 或 -initWithCoder: 81 | * 直接调用 init 会忽略相关属性的设置,所以不能直接调用 init 初始化. 82 | */ 83 | - (instancetype)init NS_UNAVAILABLE; 84 | 85 | /** 86 | * 对应UILabel的attributedText属性 87 | */ 88 | @property (readwrite, nonatomic, copy) NSAttributedString *attributedText; 89 | /** 90 | * 对应UILabel的text属性 91 | */ 92 | @property (readwrite, nonatomic, copy) id text; 93 | /** 94 | * 是否加大点击响应范围,类似于UIWebView的链点点击效果,默认NO 95 | */ 96 | @property (readwrite, nonatomic, assign) IBInspectable BOOL extendsLinkTouchArea; 97 | /** 98 | * 阴影模糊半径,值为0表示没有模糊,值越大越模糊,该值不能为负, 默认值为0。 99 | * 可与 `shadowColor`、`shadowOffset` 配合设置 100 | */ 101 | @property (readwrite, nonatomic, assign) CGFloat shadowRadius; 102 | /** 103 | * 绘制文本的内边距,默认UIEdgeInsetsZero 104 | */ 105 | @property (readwrite, nonatomic, assign) UIEdgeInsets textInsets; 106 | /** 107 | * 当text rect 小于 label frame 时文本在垂直方向的对齐方式,默认 CJVerticalAlignmentCenter 108 | */ 109 | @property (readwrite, nonatomic, assign) CJLabelVerticalAlignment verticalAlignment; 110 | /** 111 | 点击链点代理对象 112 | */ 113 | @property (readwrite, nonatomic, weak) id delegate; 114 | /** 115 | 是否支持选择复制,默认NO 116 | */ 117 | @property (readwrite, nonatomic, assign) IBInspectable BOOL enableCopy; 118 | 119 | /** 120 | 设置`self.lineBreakMode`时候的自定义字符,默认值为"…" 121 | 只针对`self.lineBreakMode`的以下三种值有效 122 | NSLineBreakByTruncatingHead, // Truncate at head of line: "…wxyz" 123 | NSLineBreakByTruncatingTail, // Truncate at tail of line: "abcd…" 124 | NSLineBreakByTruncatingMiddle // Truncate middle of line: "ab…yz" 125 | */ 126 | @property (readwrite, nonatomic, strong) NSAttributedString *attributedTruncationToken; 127 | 128 | /** 129 | 根据NSAttributedString计算CJLabel的size大小 130 | 131 | @param attributedString NSAttributedString字符串 132 | @param size 预计大小(比如:CGSizeMake(320, CGFLOAT_MAX)) 133 | @param numberOfLines 指定行数(0表示不限制) 134 | @return 结果size 135 | */ 136 | + (CGSize)sizeWithAttributedString:(NSAttributedString *)attributedString 137 | withConstraints:(CGSize)size 138 | limitedToNumberOfLines:(NSUInteger)numberOfLines; 139 | 140 | 141 | /** 142 | 根据NSAttributedString计算CJLabel的size大小 143 | 144 | @param attributedString NSAttributedString字符串 145 | @param size 预计大小(比如:CGSizeMake(320, CGFLOAT_MAX)) 146 | @param numberOfLines 指定行数(0表示不限制) 147 | @param textInsets CJLabel的内边距 148 | @return 结果size 149 | */ 150 | + (CGSize)sizeWithAttributedString:(NSAttributedString *)attributedString 151 | withConstraints:(CGSize)size 152 | limitedToNumberOfLines:(NSUInteger)numberOfLines 153 | textInsets:(UIEdgeInsets)textInsets; 154 | 155 | /** 156 | 初始化配置实例 157 | 158 | @param attributes 普通属性 159 | @param isLink 是否是点击链点 160 | @param activeLinkAttributes 点击高亮属性 161 | @param parameter 链点参数 162 | @param clickLinkBlock 链点点击block 163 | @param longPressBlock 链点长按block 164 | @return CJLabelConfigure实例 165 | */ 166 | + (CJLabelConfigure *)configureAttributes:(NSDictionary *)attributes 167 | isLink:(BOOL)isLink 168 | activeLinkAttributes:(NSDictionary *)activeLinkAttributes 169 | parameter:(id)parameter 170 | clickLinkBlock:(CJLabelLinkModelBlock)clickLinkBlock 171 | longPressBlock:(CJLabelLinkModelBlock)longPressBlock; 172 | 173 | /** 174 | 根据图片名或UIImage初始化NSAttributedString 175 | (废弃原因:该方法显示图片只能是UIViewContentModeScaleToFill模式,而且不能更改) 176 | */ 177 | + (NSMutableAttributedString *)initWithImage:(id)image 178 | imageSize:(CGSize)size 179 | imagelineAlignment:(CJLabelVerticalAlignment)lineAlignment 180 | configure:(CJLabelConfigure *)configure __deprecated_msg("Use + initWithView:viewSize:lineAlignment:configure: instead"); 181 | 182 | /** 183 | 根据自定义View初始化NSAttributedString 184 | 185 | @param view 需要插入的view(包括UIImage,NSString图片名称,UIView) 186 | @param size view的显示区域大小 187 | @param lineAlignment 插入view所在行,与文字在垂直方向的对齐方式(只针对当前行) 188 | @param configure 链点配置 189 | @return NSAttributedString 190 | */ 191 | + (NSMutableAttributedString *)initWithView:(id)view 192 | viewSize:(CGSize)size 193 | lineAlignment:(CJLabelVerticalAlignment)lineAlignment 194 | configure:(CJLabelConfigure *)configure; 195 | 196 | /** 197 | 在指定位置插入图片,并设置图片链点属性(已废弃,废弃原因:该方法显示图片只能是UIViewContentModeScaleToFill模式,而且不能更改) 198 | 注意!!!插入图片, 如果设置 NSParagraphStyleAttributeName 属性, 199 | 请保证 paragraph.lineBreakMode = NSLineBreakByCharWrapping,不然当Label的宽度不够显示内容或图片时,不会自动换行, 部分图片将会看不见 200 | 默认 paragraph.lineBreakMode = NSLineBreakByCharWrapping 201 | */ 202 | + (NSMutableAttributedString *)insertImageAtAttrString:(NSAttributedString *)attrStr 203 | image:(id)image 204 | imageSize:(CGSize)size 205 | atIndex:(NSUInteger)loc 206 | imagelineAlignment:(CJLabelVerticalAlignment)lineAlignment 207 | configure:(CJLabelConfigure *)configure __deprecated_msg("Use + insertViewAtAttrString:view:viewSize:atIndex:imagelineAlignment:configure: instead"); 208 | 209 | /** 210 | 在指定位置插入任意UIView 211 | 212 | 注意!!! 213 | 1、插入任意View, 如果设置 NSParagraphStyleAttributeName 属性, 214 | 请保证 paragraph.lineBreakMode = NSLineBreakByCharWrapping,不然当Label的宽度不够显示内容或view时,不会自动换行, 部分view将会看不见 215 | 默认 paragraph.lineBreakMode = NSLineBreakByCharWrapping 216 | 2、插入任意View,如果 configure.isLink = YES,那么将优先响应CJLabel的点击响应 217 | 218 | @param attrStr 源字符串 219 | @param view 需要插入的view(包括UIImage,NSString图片名称,UIView) 220 | @param size view的显示区域大小 221 | @param loc 插入位置 222 | @param lineAlignment 插入view所在行,与文字在垂直方向的对齐方式(只针对当前行) 223 | @param configure 链点配置 224 | @return NSAttributedString 225 | */ 226 | + (NSMutableAttributedString *)insertViewAtAttrString:(NSAttributedString *)attrStr 227 | view:(id)view 228 | viewSize:(CGSize)size 229 | atIndex:(NSUInteger)loc 230 | lineAlignment:(CJLabelVerticalAlignment)lineAlignment 231 | configure:(CJLabelConfigure *)configure; 232 | 233 | /** 234 | 设置指定NSRange属性 235 | 236 | @param attrStr 需要设置的源NSAttributedString 237 | @param range 指定NSRange 238 | @param configure 链点配置 239 | @return NSAttributedString 240 | */ 241 | + (NSMutableAttributedString *)configureAttrString:(NSAttributedString *)attrStr 242 | atRange:(NSRange)range 243 | configure:(CJLabelConfigure *)configure; 244 | 245 | /** 246 | 根据NSString初始化NSAttributedString 247 | */ 248 | + (NSMutableAttributedString *)initWithString:(NSString *)string configure:(CJLabelConfigure *)configure; 249 | 250 | 251 | /** 252 | 根据NSString初始化NSAttributedString 253 | 254 | @param string 指定的NSString 255 | @param strIdentifier 设置链点的唯一标识(用来区分不同的NSString,比如重名的 "@王小明" ,此时代表了不同的用户,不应该设置相同属性) 256 | @param configure 链点配置 257 | @return NSAttributedString 258 | */ 259 | + (NSMutableAttributedString *)initWithNSString:(NSString *)string 260 | strIdentifier:(NSString *)strIdentifier 261 | configure:(CJLabelConfigure *)configure; 262 | 263 | /** 264 | 对跟string相同的文本设置链点属性 265 | 266 | @param attrStr 需要设置的源NSAttributedString 267 | @param string 指定字符串 268 | @param configure 链点配置 269 | @param sameStringEnable 文本中所有与string相同的文本是否同步设置属性,sameStringEnable=NO 时取文本中首次匹配的NSAttributedString 270 | @return NSAttributedString 271 | */ 272 | + (NSMutableAttributedString *)configureAttrString:(NSAttributedString *)attrStr 273 | withString:(NSString *)string 274 | sameStringEnable:(BOOL)sameStringEnable 275 | configure:(CJLabelConfigure *)configure; 276 | 277 | /** 278 | 根据NSAttributedString初始化NSAttributedString 279 | 280 | @param attributedString 指定的NSAttributedString 281 | @param configure 链点配置 282 | @param strIdentifier 设置链点的唯一标识(用来区分不同的NSAttributedString,比如重名的 "@王小明" ,此时代表了不同的用户,不应该设置相同属性) 283 | @return NSAttributedString 284 | */ 285 | + (NSMutableAttributedString *)initWithAttributedString:(NSAttributedString *)attributedString 286 | strIdentifier:(NSString *)strIdentifier 287 | configure:(CJLabelConfigure *)configure; 288 | 289 | /** 290 | 对指定strIdentifier标识的attributedString设置链点属性,如果存在多个相同的文本,可以同时设置 291 | 292 | @param attrString 需要设置的源NSAttributedString 293 | @param attributedString 指定的NSAttributedString 294 | @param strIdentifier 设置链点的唯一标识(用来区分不同的NSAttributedString,比如重名的 "@王小明" ,此时代表了不同的用户,不应该设置相同属性) 295 | @param sameStringEnable 文本中相同的NSAttributedString是否同步设置属性,sameStringEnable=NO 时取文本中首次匹配的NSAttributedString 296 | @return NSAttributedString 297 | */ 298 | + (NSMutableAttributedString *)configureAttrString:(NSAttributedString *)attrString 299 | withAttributedString:(NSAttributedString *)attributedString 300 | strIdentifier:(NSString *)strIdentifier 301 | sameStringEnable:(BOOL)sameStringEnable 302 | configure:(CJLabelConfigure *)configure; 303 | 304 | 305 | /// 根据NSAttributedString初始化不可换行的NSAttributedString 306 | /// @param attString 指定的NSAttributedString 307 | /// @param textInsets 不可换行字符的内边距 308 | /// @param configure 链点配置 309 | + (NSMutableAttributedString *)initWithNonLineWrapAttributedString:(NSAttributedString *)attString 310 | textInsets:(UIEdgeInsets)textInsets 311 | configure:(CJLabelConfigure *)configure; 312 | 313 | /** 314 | 获取指定NSAttributedString中跟linkString相同的NSValue数组,NSValue值为对应的NSRange 315 | 316 | @param linkString 需要寻找的string 317 | @param attString 指定NSAttributedString 318 | @return NSRange的数组 319 | */ 320 | + (NSArray *)sameLinkStringRangeArray:(NSString *)linkString inAttString:(NSAttributedString *)attString; 321 | 322 | /** 323 | 获取指定NSAttributedString中跟linkAttString相同的NSRange数组,NSValue值为对应的NSRange 324 | 325 | @param linkAttString 需要寻找的NSAttributedString 326 | @param strIdentifier 链点标识 327 | @param attString 指定NSAttributedString 328 | @return NSRange的数组 329 | */ 330 | + (NSArray *)samelinkAttStringRangeArray:(NSAttributedString *)linkAttString strIdentifier:(NSString *)strIdentifier inAttString:(NSAttributedString *)attString; 331 | 332 | 333 | /** 334 | * 移除指定range的点击链点 335 | * 336 | * @param range 移除链点位置 337 | * 338 | * @return 返回新的NSAttributedString 339 | */ 340 | - (NSAttributedString *)removeLinkAtRange:(NSRange)range; 341 | 342 | /** 343 | * 移除所有点击链点 344 | * 345 | * @return 返回新的NSAttributedString 346 | */ 347 | - (NSAttributedString *)removeAllLink; 348 | 349 | /** 350 | 刷新文本 351 | */ 352 | - (void)flushText; 353 | 354 | @end 355 | 356 | 357 | /** 358 | 背景填充颜色。值为UIColor。默认 `nil`。 359 | 该属性优先级低于NSBackgroundColorAttributeName,如果设置NSBackgroundColorAttributeName会覆盖kCJBackgroundFillColorAttributeName 360 | */ 361 | extern NSString * const kCJBackgroundFillColorAttributeName; 362 | 363 | /** 364 | 背景边框线颜色。值为UIColor。默认 `nil` 365 | */ 366 | extern NSString * const kCJBackgroundStrokeColorAttributeName; 367 | 368 | /** 369 | 背景边框线宽度。值为NSNumber。默认 `1.0f` 370 | */ 371 | extern NSString * const kCJBackgroundLineWidthAttributeName; 372 | 373 | /** 374 | 背景边框线圆角角度。值为NSNumber。默认 `5.0f` 375 | */ 376 | extern NSString * const kCJBackgroundLineCornerRadiusAttributeName; 377 | 378 | /** 379 | 点击时候的背景填充颜色。值为UIColor。默认 `nil`。 380 | 该属性优先级低于NSBackgroundColorAttributeName,如果设置NSBackgroundColorAttributeName会覆盖kCJActiveBackgroundFillColorAttributeName 381 | */ 382 | extern NSString * const kCJActiveBackgroundFillColorAttributeName; 383 | 384 | /** 385 | 点击时候的背景边框线颜色。值为UIColor。默认 `nil` 386 | */ 387 | extern NSString * const kCJActiveBackgroundStrokeColorAttributeName; 388 | 389 | /** 390 | 删除线宽度。值为NSNumber。默认 `0.0f`,表示无删除线 391 | */ 392 | extern NSString * const kCJStrikethroughStyleAttributeName; 393 | 394 | /** 395 | 删除线颜色。值为UIColor。默认 `[UIColor blackColor]`。 396 | */ 397 | extern NSString * const kCJStrikethroughColorAttributeName; 398 | 399 | /** 400 | 对NSAttributedString文本设置链点属性时候的唯一标识 401 | */ 402 | extern NSString * const kCJLinkStringIdentifierAttributesName; 403 | -------------------------------------------------------------------------------- /CJLabel/CJLabelConfigure.h: -------------------------------------------------------------------------------- 1 | // 2 | // CJLabelConfigure.h 3 | // CJLabelTest 4 | // 5 | // Created by ChiJinLian on 2017/4/13. 6 | // Copyright © 2017年 ChiJinLian. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import 12 | @class CJLabelLinkModel; 13 | @class CJLabel; 14 | 15 | static char kAssociatedUITouchKey; 16 | 17 | extern NSString * const kCJInsertViewTag; 18 | 19 | #define CJLabelIsNull(a) ((a)==nil || (a)==NULL || (NSNull *)(a)==[NSNull null]) 20 | #define CJUIRGBColor(r,g,b,a) ([UIColor colorWithRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:a]) 21 | 22 | typedef void (^CJLabelLinkModelBlock)(CJLabelLinkModel *linkModel); 23 | 24 | /** 25 | 当text bounds小于label bounds时,文本的垂直对齐方式 26 | */ 27 | typedef NS_ENUM(NSInteger, CJLabelVerticalAlignment) { 28 | CJVerticalAlignmentCenter = 0,//垂直居中 29 | CJVerticalAlignmentTop = 1,//居上 30 | CJVerticalAlignmentBottom = 2,//靠下 31 | }; 32 | 33 | /** 34 | 当CTLine包含插入图片时,描述当前行文字在垂直方向的对齐方式 35 | */ 36 | struct CJCTLineVerticalLayout { 37 | CFIndex line;//第几行 38 | CGFloat lineAscentAndDescent;//上行高和下行高之和 39 | CGRect lineRect;//当前行对应的CGRect 40 | 41 | CGFloat maxRunHeight;//当前行run的最大高度(不包括图片) 42 | CGFloat maxRunAscent;//CTRun的最大上行高 43 | 44 | CGFloat maxImageHeight;//图片的最大高度 45 | CGFloat maxImageAscent;//图片的最大上行高 46 | 47 | CJLabelVerticalAlignment verticalAlignment;//对齐方式(默认底部对齐) 48 | }; 49 | typedef struct CJCTLineVerticalLayout CJCTLineVerticalLayout; 50 | 51 | /** 52 | 设置链点属性辅助类,可设置链点正常属性、点击高亮属性、链点自定义参数、点击回调以及长按回调 53 | */ 54 | @interface CJLabelConfigure : NSObject 55 | /** 56 | 设置链点的自定义属性 57 | */ 58 | @property (nonatomic, strong) NSDictionary *attributes; 59 | /** 60 | 是否为可点击链点,设置 isLink=YES 时,activeLinkAttributes、parameter、clickLinkBlock、longPressBlock才有效 61 | */ 62 | @property (nonatomic, assign) BOOL isLink; 63 | /** 64 | 设置链点点击高亮时候的自定义属性 65 | */ 66 | @property (nonatomic, strong) NSDictionary *activeLinkAttributes; 67 | /** 68 | 点击链点的自定义参数 69 | */ 70 | @property (nonatomic, strong) id parameter; 71 | /** 72 | 点击链点回调block 73 | */ 74 | @property (nonatomic, copy) CJLabelLinkModelBlock clickLinkBlock; 75 | /** 76 | 长按链点的回调block 77 | */ 78 | @property (nonatomic, copy) CJLabelLinkModelBlock longPressBlock; 79 | 80 | /** 81 | 添加 attributes 属性 82 | 83 | @param attributes 属性值 84 | @param key 属性 85 | */ 86 | - (void)addAttributes:(id)attributes key:(NSString *)key; 87 | /** 88 | 移除 attributes 指定属性 89 | 90 | @param key 属性 91 | */ 92 | - (void)removeAttributesForKey:(NSString *)key; 93 | /** 94 | 添加 activeAttributes 属性 95 | 96 | @param activeAttributes 属性值 97 | @param key 属性 98 | */ 99 | - (void)addActiveAttributes:(id)activeAttributes key:(NSString *)key; 100 | /** 101 | 移除 activeAttributes 指定属性 102 | 103 | @param key 属性 104 | */ 105 | - (void)removeActiveLinkAttributesForKey:(NSString *)key; 106 | 107 | /** 108 | 在指定位置插入图片链点!!! 109 | */ 110 | + (NSMutableAttributedString *)configureLinkAttributedString:(NSAttributedString *)attrStr 111 | addImage:(id)image 112 | imageSize:(CGSize)size 113 | atIndex:(NSUInteger)loc 114 | verticalAlignment:(CJLabelVerticalAlignment)verticalAlignment 115 | linkAttributes:(NSDictionary *)linkAttributes 116 | activeLinkAttributes:(NSDictionary *)activeLinkAttributes 117 | parameter:(id)parameter 118 | clickLinkBlock:(CJLabelLinkModelBlock)clickLinkBlock 119 | longPressBlock:(CJLabelLinkModelBlock)longPressBlock 120 | islink:(BOOL)isLink; 121 | /** 122 | 根据指定NSRange配置富文本链点!!! 123 | */ 124 | + (NSMutableAttributedString *)configureLinkAttributedString:(NSAttributedString *)attrStr 125 | atRange:(NSRange)range 126 | linkAttributes:(NSDictionary *)linkAttributes 127 | activeLinkAttributes:(NSDictionary *)activeLinkAttributes 128 | parameter:(id)parameter 129 | clickLinkBlock:(CJLabelLinkModelBlock)clickLinkBlock 130 | longPressBlock:(CJLabelLinkModelBlock)longPressBlock 131 | islink:(BOOL)isLink; 132 | 133 | 134 | /** 135 | 对文本中跟withString相同的文字配置富文本链点!!! 136 | */ 137 | + (NSMutableAttributedString *)configureLinkAttributedString:(NSAttributedString *)attrStr 138 | withString:(NSString *)withString 139 | sameStringEnable:(BOOL)sameStringEnable 140 | linkAttributes:(NSDictionary *)linkAttributes 141 | activeLinkAttributes:(NSDictionary *)activeLinkAttributes 142 | parameter:(id)parameter 143 | clickLinkBlock:(CJLabelLinkModelBlock)clickLinkBlock 144 | longPressBlock:(CJLabelLinkModelBlock)longPressBlock 145 | islink:(BOOL)isLink; 146 | 147 | /** 148 | 对文本中跟withAttString相同的NSAttributedString配置富文本链点!!! 149 | */ 150 | + (NSMutableAttributedString *)configureLinkAttributedString:(NSAttributedString *)attrStr 151 | withAttString:(NSAttributedString *)withAttString 152 | sameStringEnable:(BOOL)sameStringEnable 153 | linkAttributes:(NSDictionary *)linkAttributes 154 | activeLinkAttributes:(NSDictionary *)activeLinkAttributes 155 | parameter:(id)parameter 156 | clickLinkBlock:(CJLabelLinkModelBlock)clickLinkBlock 157 | longPressBlock:(CJLabelLinkModelBlock)longPressBlock 158 | islink:(BOOL)isLink; 159 | /** 160 | 对文本中指定strIdentifier标识的NSAttributedString配置富文本链点!!! 161 | */ 162 | + (NSMutableAttributedString *)configureAttrString:(NSAttributedString *)attrString 163 | strIdentifier:(NSString *)strIdentifier 164 | configure:(CJLabelConfigure *)configure 165 | linkRangeAry:(NSArray *)linkRangeAry; 166 | 167 | /** 168 | 生成string链点的NSAttributedString(请保证identifier的唯一性!!!) 169 | 170 | @param string 点击链点的string 171 | @param attrs 链点属性 172 | @param identifier 点击链点的唯一标识 173 | @return 返回点击链点的NSMutableAttributedString 174 | */ 175 | + (NSMutableAttributedString *)linkAttStr:(NSString *)string 176 | attributes:(NSDictionary *)attrs 177 | identifier:(NSString *)identifier; 178 | 179 | + (NSArray *)getLinkStringRangeArray:(NSString *)linkString inAttString:(NSAttributedString *)attString; 180 | + (NSArray *)getLinkAttStringRangeArray:(NSAttributedString *)linkAttString inAttString:(NSAttributedString *)attString; 181 | 182 | @end 183 | 184 | /** 185 | 点击链点model 186 | */ 187 | @interface CJLabelLinkModel : NSObject 188 | /** 189 | 当前CJLabel实例 190 | */ 191 | @property (readonly, nonatomic, strong) CJLabel *label; 192 | /** 193 | 链点文本 194 | */ 195 | @property (readonly, nonatomic, strong) NSAttributedString *attributedString; 196 | /** 197 | 链点自定义参数 198 | */ 199 | @property (readonly, nonatomic, strong) id parameter; 200 | /** 201 | 链点在整体文本中的range值 202 | */ 203 | @property (readonly, nonatomic, assign) NSRange linkRange; 204 | /** 205 | 链点view的Rect(相对于CJLabel坐标的rect) 206 | */ 207 | @property (readonly, nonatomic, assign) CGRect insertViewRect; 208 | /** 209 | 插入链点view 210 | */ 211 | @property (readonly, nonatomic, strong) id insertView; 212 | 213 | - (instancetype)initWithAttributedString:(NSAttributedString *)attributedString 214 | insertView:(id)insertView 215 | insertViewRect:(CGRect)insertViewRect 216 | parameter:(id)parameter 217 | linkRange:(NSRange)linkRange 218 | label:(CJLabel *)label; 219 | @end 220 | 221 | /** 222 | 响应点击以及指定区域绘制边框线辅助类 223 | */ 224 | @interface CJGlyphRunStrokeItem : NSObject 225 | 226 | @property (nonatomic, strong) UIColor *fillColor;//填充背景色 227 | @property (nonatomic, strong) UIColor *strokeColor;//描边边框色 228 | @property (nonatomic, strong) UIColor *activeFillColor;//点击选中时候的填充背景色 229 | @property (nonatomic, strong) UIColor *activeStrokeColor;//点击选中时候的描边边框色 230 | @property (nonatomic, assign) CGFloat strokeLineWidth;//描边边框粗细 231 | @property (nonatomic, assign) CGFloat cornerRadius;//描边圆角 232 | @property (nonatomic, assign) CGFloat strikethroughStyle;//删除线 233 | @property (nonatomic, strong) UIColor *strikethroughColor;//删除线颜色 234 | @property (nonatomic, assign) CGRect runBounds;//描边区域在系统坐标下的rect(原点在左下角) 235 | @property (nonatomic, assign) CGRect locBounds;//描边区域在屏幕坐标下的rect(原点在左上角),相同的一组CTRun,发生了合并 236 | @property (nonatomic, assign) CGRect withOutMergeBounds;//每个字符对应的CTRun 在屏幕坐标下的rect(原点在左上角),没有发生合并 237 | 238 | @property (nonatomic, assign) CGFloat runDescent;//对应的CTRun的下行高 239 | @property (nonatomic, assign) CTRunRef runRef;//对应的CTRun 240 | 241 | @property (nonatomic, strong) id insertView;//插入view 242 | @property (nonatomic, assign) BOOL isInsertView;//是否是插入view 243 | @property (nonatomic, assign) NSRange range;//链点在文本中的range 244 | @property (nonatomic, strong) id parameter;//链点自定义参数 245 | @property (nonatomic, assign) CJCTLineVerticalLayout lineVerticalLayout;//所在CTLine的行高信息结构体 246 | @property (nonatomic, assign) BOOL isLink;//判断是否为点击链点 247 | @property (nonatomic, assign) BOOL needRedrawn;//标记点击该链点是否需要重绘文本 248 | @property (nonatomic, copy) CJLabelLinkModelBlock linkBlock;//点击链点回调 249 | @property (nonatomic, copy) CJLabelLinkModelBlock longPressBlock;//长按点击链点回调 250 | /** 是否不允许换行的字符*/ 251 | @property (nonatomic, assign) BOOL isNonLineWrap; 252 | 253 | //与选择复制相关的属性 254 | @property (nonatomic, assign) NSInteger characterIndex;//字符在整段文本中的index值 255 | @property (nonatomic, assign) NSRange characterRange;//字符在整段文本中的range值 256 | 257 | @end 258 | 259 | @interface CJCTLineLayoutModel : NSObject 260 | @property (nonatomic, assign) CFIndex lineIndex;//第几行 261 | @property (nonatomic, assign) CJCTLineVerticalLayout lineVerticalLayout;//所在CTLine的行高信息结构体 262 | 263 | @property (nonatomic, assign) CGFloat selectCopyBackY;//选中后被填充背景色的复制视图的Y坐标 264 | @property (nonatomic, assign) CGFloat selectCopyBackHeight;//选中后被填充背景色的复制视图的高度 265 | @end 266 | 267 | /** 268 | 长按时候显示的放大镜视图 269 | */ 270 | @interface CJMagnifierView : UIView 271 | @end 272 | /** 273 | 选择复制时候的操作视图 274 | CJSelectBackView 在 window 层,全局只有一个 275 | CJSelectTextRangeView(选中填充背景色的view)在 CJSelectBackView上,CJMagnifierView(放大镜)则在window上 276 | */ 277 | @interface CJSelectCopyManagerView : UIView 278 | @property (nonatomic, strong) CJMagnifierView *magnifierView;//放大镜 279 | + (instancetype)instance; 280 | 281 | /** 282 | CJLabel选中point点对应的文本内容 283 | 284 | @param label CJLabel 285 | @param point 放大点 286 | @param item 放大点对应的CJGlyphRunStrokeItem 287 | @param maxLineWidth CJLabel的最大行宽度 288 | @param allCTLineVerticalArray CJLabel的CTLine数组 289 | @param allRunItemArray CJLabel的CTRun数组 290 | @param hideViewBlock 复制选择view隐藏后的block 291 | */ 292 | - (void)showSelectViewInCJLabel:(CJLabel *)label 293 | atPoint:(CGPoint)point 294 | runItem:(CJGlyphRunStrokeItem *)item 295 | maxLineWidth:(CGFloat)maxLineWidth 296 | allCTLineVerticalArray:(NSArray *)allCTLineVerticalArray 297 | allRunItemArray:(NSArray *)allRunItemArray 298 | hideViewBlock:(void(^)(void))hideViewBlock; 299 | 300 | /** 301 | 显示放大镜 302 | 303 | @param label CJLabel 304 | @param point 放大点 305 | @param runItem 放大点对应的CJGlyphRunStrokeItem 306 | */ 307 | - (void)showMagnifyInCJLabel:(CJLabel *)label 308 | magnifyPoint:(CGPoint)point 309 | runItem:(CJGlyphRunStrokeItem *)runItem; 310 | 311 | /** 312 | 隐藏选择复制相关的view 313 | */ 314 | - (void)hideView; 315 | 316 | + (CJGlyphRunStrokeItem *)currentItem:(CGPoint)point allRunItemArray:(NSArray *)allRunItemArray inset:(CGFloat)inset; 317 | @end 318 | 319 | /// 混排插入任意view时的背景view 320 | @interface CJInsertBackView : UIView 321 | @end 322 | 323 | extern NSString * const kCJImageAttributeName; 324 | extern NSString * const kCJImage; 325 | extern NSString * const kCJImageHeight; 326 | extern NSString * const kCJImageWidth; 327 | extern NSString * const kCJImageLineVerticalAlignment; 328 | 329 | extern NSString * const kCJLinkAttributesName; 330 | extern NSString * const kCJActiveLinkAttributesName; 331 | extern NSString * const kCJIsLinkAttributesName; 332 | //点击链点唯一标识 333 | extern NSString * const kCJLinkIdentifierAttributesName; 334 | extern NSString * const kCJLinkLengthAttributesName; 335 | extern NSString * const kCJLinkRangeAttributesName; 336 | extern NSString * const kCJLinkParameterAttributesName; 337 | extern NSString * const kCJClickLinkBlockAttributesName; 338 | extern NSString * const kCJLongPressBlockAttributesName; 339 | extern NSString * const kCJLinkNeedRedrawnAttributesName; 340 | /** 341 | 不允许换行字符 342 | */ 343 | extern NSString * const kCJNonLineWrapAttributesName; 344 | 345 | static CGFloat const CJFLOAT_MAX = 100000; 346 | 347 | static inline CGFLOAT_TYPE CGFloat_ceil(CGFLOAT_TYPE cgfloat) { 348 | #if CGFLOAT_IS_DOUBLE 349 | return ceil(cgfloat); 350 | #else 351 | return ceilf(cgfloat); 352 | #endif 353 | } 354 | 355 | static inline CGFLOAT_TYPE CGFloat_floor(CGFLOAT_TYPE cgfloat) { 356 | #if CGFLOAT_IS_DOUBLE 357 | return floor(cgfloat); 358 | #else 359 | return floorf(cgfloat); 360 | #endif 361 | } 362 | 363 | static inline CGFloat CJFlushFactorForTextAlignment(NSTextAlignment textAlignment) { 364 | switch (textAlignment) { 365 | case NSTextAlignmentCenter: 366 | return 0.5f; 367 | case NSTextAlignmentRight: 368 | return 1.0f; 369 | case NSTextAlignmentLeft: 370 | default: 371 | return 0.0f; 372 | } 373 | } 374 | 375 | static inline CGColorRef CGColorRefFromColor(id color) { 376 | return [color isKindOfClass:[UIColor class]] ? [color CGColor] : (__bridge CGColorRef)color; 377 | } 378 | 379 | static inline NSAttributedString * NSAttributedStringByScalingFontSize(NSAttributedString *attributedString, CGFloat scale) { 380 | NSMutableAttributedString *mutableAttributedString = [attributedString mutableCopy]; 381 | [mutableAttributedString enumerateAttribute:(NSString *)kCTFontAttributeName inRange:NSMakeRange(0, [mutableAttributedString length]) options:0 usingBlock:^(id value, NSRange range, BOOL * __unused stop) { 382 | UIFont *font = (UIFont *)value; 383 | if (font) { 384 | NSString *fontName; 385 | CGFloat pointSize; 386 | 387 | if ([font isKindOfClass:[UIFont class]]) { 388 | fontName = font.fontName; 389 | pointSize = font.pointSize; 390 | } else { 391 | fontName = (NSString *)CFBridgingRelease(CTFontCopyName((__bridge CTFontRef)font, kCTFontPostScriptNameKey)); 392 | pointSize = CTFontGetSize((__bridge CTFontRef)font); 393 | } 394 | 395 | [mutableAttributedString removeAttribute:(NSString *)kCTFontAttributeName range:range]; 396 | CTFontRef fontRef = CTFontCreateWithName((__bridge CFStringRef)fontName, CGFloat_floor(pointSize * scale), NULL); 397 | [mutableAttributedString addAttribute:(NSString *)kCTFontAttributeName value:(__bridge id)fontRef range:range]; 398 | CFRelease(fontRef); 399 | } 400 | }]; 401 | 402 | return mutableAttributedString; 403 | } 404 | 405 | static inline CGSize CTFramesetterSuggestFrameSizeForAttributedStringWithConstraints(CTFramesetterRef framesetter, NSAttributedString *attributedString, CGSize size, NSUInteger numberOfLines) { 406 | CFRange rangeToSize = CFRangeMake(0, (CFIndex)[attributedString length]); 407 | CGSize constraints = CGSizeMake(size.width, CJFLOAT_MAX); 408 | 409 | if (numberOfLines == 1) { 410 | constraints = CGSizeMake(CJFLOAT_MAX, CJFLOAT_MAX); 411 | } else if (numberOfLines > 0) { 412 | CGMutablePathRef path = CGPathCreateMutable(); 413 | CGPathAddRect(path, NULL, CGRectMake(0.0f, 0.0f, constraints.width, CJFLOAT_MAX)); 414 | CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, NULL); 415 | CFArrayRef lines = CTFrameGetLines(frame); 416 | 417 | if (CFArrayGetCount(lines) > 0) { 418 | NSInteger lastVisibleLineIndex = MIN((CFIndex)numberOfLines, CFArrayGetCount(lines)) - 1; 419 | CTLineRef lastVisibleLine = CFArrayGetValueAtIndex(lines, lastVisibleLineIndex); 420 | 421 | CFRange rangeToLayout = CTLineGetStringRange(lastVisibleLine); 422 | rangeToSize = CFRangeMake(0, rangeToLayout.location + rangeToLayout.length); 423 | } 424 | 425 | CFRelease(frame); 426 | CGPathRelease(path); 427 | } 428 | 429 | CGSize suggestedSize = CTFramesetterSuggestFrameSizeWithConstraints(framesetter, rangeToSize, NULL, constraints, NULL); 430 | 431 | return CGSizeMake(CGFloat_ceil(suggestedSize.width), CGFloat_ceil(suggestedSize.height)); 432 | }; 433 | 434 | static inline CGFloat compareMaxNum(CGFloat firstNum, CGFloat secondNum, BOOL max){ 435 | CGFloat result = firstNum; 436 | if (max) { 437 | result = (firstNum >= secondNum)?firstNum:secondNum; 438 | }else{ 439 | result = (firstNum <= secondNum)?firstNum:secondNum; 440 | } 441 | return result; 442 | } 443 | 444 | static inline UIColor * colorWithAttributeName(NSDictionary *dic, NSString *key){ 445 | UIColor *color = nil; 446 | if (dic[key] && nil != dic[key]) { 447 | color = dic[key]; 448 | } 449 | return color; 450 | } 451 | 452 | static inline BOOL isNotClearColor(UIColor *color){ 453 | if (CJLabelIsNull(color)) { 454 | return NO; 455 | } 456 | BOOL notClearColor = YES; 457 | if (CGColorEqualToColor(color.CGColor, [UIColor clearColor].CGColor)) { 458 | notClearColor = NO; 459 | } 460 | return notClearColor; 461 | } 462 | 463 | static inline BOOL isSameColor(UIColor *color1, UIColor *color2){ 464 | BOOL same = YES; 465 | if (!CGColorEqualToColor(color1.CGColor, color2.CGColor)) { 466 | same = NO; 467 | } 468 | return same; 469 | } 470 | 471 | static inline UIWindow * CJkeyWindow() { 472 | UIApplication *app = [UIApplication sharedApplication]; 473 | if ([app.delegate respondsToSelector:@selector(window)]) { 474 | return [app.delegate window]; 475 | } else { 476 | return [app keyWindow]; 477 | } 478 | } 479 | 480 | -------------------------------------------------------------------------------- /Example/CJLabelTest.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 4360A64323E6C30D003920A1 /* HealthKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4360A64223E6C30D003920A1 /* HealthKit.framework */; }; 11 | 438257861C1A6F5F00A3DA24 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 438257851C1A6F5F00A3DA24 /* main.m */; }; 12 | 438257891C1A6F5F00A3DA24 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 438257881C1A6F5F00A3DA24 /* AppDelegate.m */; }; 13 | 4382578C1C1A6F5F00A3DA24 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4382578B1C1A6F5F00A3DA24 /* ViewController.m */; }; 14 | 4382578F1C1A6F5F00A3DA24 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4382578D1C1A6F5F00A3DA24 /* Main.storyboard */; }; 15 | 438257911C1A6F5F00A3DA24 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 438257901C1A6F5F00A3DA24 /* Assets.xcassets */; }; 16 | 438257941C1A6F5F00A3DA24 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 438257921C1A6F5F00A3DA24 /* LaunchScreen.storyboard */; }; 17 | 4382579F1C1A6F5F00A3DA24 /* CJLabelTestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4382579E1C1A6F5F00A3DA24 /* CJLabelTestTests.m */; }; 18 | 438257AA1C1A6F5F00A3DA24 /* CJLabelTestUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 438257A91C1A6F5F00A3DA24 /* CJLabelTestUITests.m */; }; 19 | 43C1652E25C92B1B00EAA38B /* CJLabel+UIMenu.m in Sources */ = {isa = PBXBuildFile; fileRef = 43C1652D25C92B1B00EAA38B /* CJLabel+UIMenu.m */; }; 20 | B40ACCA71EB30CF3006DB787 /* AttributedTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = B40ACCA41EB30CF3006DB787 /* AttributedTableViewCell.m */; }; 21 | B40ACCA81EB30CF3006DB787 /* AttributedTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = B40ACCA51EB30CF3006DB787 /* AttributedTableViewCell.xib */; }; 22 | B431D5321D0545E700228988 /* CJLabel.m in Sources */ = {isa = PBXBuildFile; fileRef = B431D52E1D0545E700228988 /* CJLabel.m */; }; 23 | B45AC7ED1F8A7181006B5336 /* CJLabelConfigure.m in Sources */ = {isa = PBXBuildFile; fileRef = B45AC7EC1F8A7181006B5336 /* CJLabelConfigure.m */; }; 24 | B483EC891FBAC7AD000FCE06 /* FirstDetailViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B483EC861FBAC7AD000FCE06 /* FirstDetailViewController.m */; }; 25 | B483EC8A1FBAC7AD000FCE06 /* FirstDetailViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = B483EC881FBAC7AD000FCE06 /* FirstDetailViewController.xib */; }; 26 | B4928B9F1FB4003D0017A25C /* Example.json in Resources */ = {isa = PBXBuildFile; fileRef = B4928B9E1FB4003D0017A25C /* Example.json */; }; 27 | B4C8931B1EB881EA00AC9E42 /* SecondDetailViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B4C893191EB881EA00AC9E42 /* SecondDetailViewController.m */; }; 28 | B4C8931C1EB881EA00AC9E42 /* SecondDetailViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = B4C8931A1EB881EA00AC9E42 /* SecondDetailViewController.xib */; }; 29 | /* End PBXBuildFile section */ 30 | 31 | /* Begin PBXContainerItemProxy section */ 32 | 4382579B1C1A6F5F00A3DA24 /* PBXContainerItemProxy */ = { 33 | isa = PBXContainerItemProxy; 34 | containerPortal = 438257791C1A6F5F00A3DA24 /* Project object */; 35 | proxyType = 1; 36 | remoteGlobalIDString = 438257801C1A6F5F00A3DA24; 37 | remoteInfo = CJLabelTest; 38 | }; 39 | 438257A61C1A6F5F00A3DA24 /* PBXContainerItemProxy */ = { 40 | isa = PBXContainerItemProxy; 41 | containerPortal = 438257791C1A6F5F00A3DA24 /* Project object */; 42 | proxyType = 1; 43 | remoteGlobalIDString = 438257801C1A6F5F00A3DA24; 44 | remoteInfo = CJLabelTest; 45 | }; 46 | /* End PBXContainerItemProxy section */ 47 | 48 | /* Begin PBXCopyFilesBuildPhase section */ 49 | E7D4B2B723B2248E00F2E528 /* Embed Frameworks */ = { 50 | isa = PBXCopyFilesBuildPhase; 51 | buildActionMask = 2147483647; 52 | dstPath = ""; 53 | dstSubfolderSpec = 10; 54 | files = ( 55 | ); 56 | name = "Embed Frameworks"; 57 | runOnlyForDeploymentPostprocessing = 0; 58 | }; 59 | /* End PBXCopyFilesBuildPhase section */ 60 | 61 | /* Begin PBXFileReference section */ 62 | 4360A64223E6C30D003920A1 /* HealthKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = HealthKit.framework; path = System/Library/Frameworks/HealthKit.framework; sourceTree = SDKROOT; }; 63 | 438257811C1A6F5F00A3DA24 /* CJLabelTest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CJLabelTest.app; sourceTree = BUILT_PRODUCTS_DIR; }; 64 | 438257851C1A6F5F00A3DA24 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 65 | 438257871C1A6F5F00A3DA24 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 66 | 438257881C1A6F5F00A3DA24 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 67 | 4382578A1C1A6F5F00A3DA24 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ViewController.h; path = ../Example/CJLabelTest/ViewController.h; sourceTree = ""; }; 68 | 4382578B1C1A6F5F00A3DA24 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = ViewController.m; path = ../Example/CJLabelTest/ViewController.m; sourceTree = ""; }; 69 | 4382578E1C1A6F5F00A3DA24 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 70 | 438257901C1A6F5F00A3DA24 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 71 | 438257931C1A6F5F00A3DA24 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 72 | 438257951C1A6F5F00A3DA24 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 73 | 4382579A1C1A6F5F00A3DA24 /* CJLabelTestTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CJLabelTestTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 74 | 4382579E1C1A6F5F00A3DA24 /* CJLabelTestTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CJLabelTestTests.m; sourceTree = ""; }; 75 | 438257A01C1A6F5F00A3DA24 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 76 | 438257A51C1A6F5F00A3DA24 /* CJLabelTestUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CJLabelTestUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 77 | 438257A91C1A6F5F00A3DA24 /* CJLabelTestUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CJLabelTestUITests.m; sourceTree = ""; }; 78 | 438257AB1C1A6F5F00A3DA24 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 79 | 4396AC9E23E6C3B800836CE2 /* CJLabelTest.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; name = CJLabelTest.entitlements; path = ../../../../../../.Trash/CJLabelTest.entitlements; sourceTree = ""; }; 80 | 43C1652C25C92B1B00EAA38B /* CJLabel+UIMenu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CJLabel+UIMenu.h"; sourceTree = ""; }; 81 | 43C1652D25C92B1B00EAA38B /* CJLabel+UIMenu.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "CJLabel+UIMenu.m"; sourceTree = ""; }; 82 | B40ACCA31EB30CF3006DB787 /* AttributedTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AttributedTableViewCell.h; sourceTree = ""; }; 83 | B40ACCA41EB30CF3006DB787 /* AttributedTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AttributedTableViewCell.m; sourceTree = ""; }; 84 | B40ACCA51EB30CF3006DB787 /* AttributedTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = AttributedTableViewCell.xib; sourceTree = ""; }; 85 | B431D52D1D0545E700228988 /* CJLabel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CJLabel.h; sourceTree = ""; }; 86 | B431D52E1D0545E700228988 /* CJLabel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CJLabel.m; sourceTree = ""; }; 87 | B45AC7EB1F8A7181006B5336 /* CJLabelConfigure.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CJLabelConfigure.h; sourceTree = ""; }; 88 | B45AC7EC1F8A7181006B5336 /* CJLabelConfigure.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CJLabelConfigure.m; sourceTree = ""; }; 89 | B483EC861FBAC7AD000FCE06 /* FirstDetailViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FirstDetailViewController.m; path = CJLabelTest/FirstDetailViewController.m; sourceTree = SOURCE_ROOT; }; 90 | B483EC871FBAC7AD000FCE06 /* FirstDetailViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FirstDetailViewController.h; path = CJLabelTest/FirstDetailViewController.h; sourceTree = SOURCE_ROOT; }; 91 | B483EC881FBAC7AD000FCE06 /* FirstDetailViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = FirstDetailViewController.xib; path = CJLabelTest/FirstDetailViewController.xib; sourceTree = SOURCE_ROOT; }; 92 | B4928B9E1FB4003D0017A25C /* Example.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = Example.json; sourceTree = ""; }; 93 | B4928BA51FB447920017A25C /* Common.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Common.h; sourceTree = ""; }; 94 | B4C893181EB881EA00AC9E42 /* SecondDetailViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SecondDetailViewController.h; path = CJLabelTest/SecondDetailViewController.h; sourceTree = SOURCE_ROOT; }; 95 | B4C893191EB881EA00AC9E42 /* SecondDetailViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SecondDetailViewController.m; path = CJLabelTest/SecondDetailViewController.m; sourceTree = SOURCE_ROOT; }; 96 | B4C8931A1EB881EA00AC9E42 /* SecondDetailViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = SecondDetailViewController.xib; path = CJLabelTest/SecondDetailViewController.xib; sourceTree = SOURCE_ROOT; }; 97 | /* End PBXFileReference section */ 98 | 99 | /* Begin PBXFrameworksBuildPhase section */ 100 | 4382577E1C1A6F5F00A3DA24 /* Frameworks */ = { 101 | isa = PBXFrameworksBuildPhase; 102 | buildActionMask = 2147483647; 103 | files = ( 104 | 4360A64323E6C30D003920A1 /* HealthKit.framework in Frameworks */, 105 | ); 106 | runOnlyForDeploymentPostprocessing = 0; 107 | }; 108 | 438257971C1A6F5F00A3DA24 /* Frameworks */ = { 109 | isa = PBXFrameworksBuildPhase; 110 | buildActionMask = 2147483647; 111 | files = ( 112 | ); 113 | runOnlyForDeploymentPostprocessing = 0; 114 | }; 115 | 438257A21C1A6F5F00A3DA24 /* Frameworks */ = { 116 | isa = PBXFrameworksBuildPhase; 117 | buildActionMask = 2147483647; 118 | files = ( 119 | ); 120 | runOnlyForDeploymentPostprocessing = 0; 121 | }; 122 | /* End PBXFrameworksBuildPhase section */ 123 | 124 | /* Begin PBXGroup section */ 125 | 4360A64123E6C30D003920A1 /* Frameworks */ = { 126 | isa = PBXGroup; 127 | children = ( 128 | 4360A64223E6C30D003920A1 /* HealthKit.framework */, 129 | ); 130 | name = Frameworks; 131 | sourceTree = ""; 132 | }; 133 | 438257781C1A6F5F00A3DA24 = { 134 | isa = PBXGroup; 135 | children = ( 136 | 438257831C1A6F5F00A3DA24 /* CJLabelTest */, 137 | 4382579D1C1A6F5F00A3DA24 /* CJLabelTestTests */, 138 | 438257A81C1A6F5F00A3DA24 /* CJLabelTestUITests */, 139 | 438257821C1A6F5F00A3DA24 /* Products */, 140 | 4360A64123E6C30D003920A1 /* Frameworks */, 141 | ); 142 | sourceTree = ""; 143 | }; 144 | 438257821C1A6F5F00A3DA24 /* Products */ = { 145 | isa = PBXGroup; 146 | children = ( 147 | 438257811C1A6F5F00A3DA24 /* CJLabelTest.app */, 148 | 4382579A1C1A6F5F00A3DA24 /* CJLabelTestTests.xctest */, 149 | 438257A51C1A6F5F00A3DA24 /* CJLabelTestUITests.xctest */, 150 | ); 151 | name = Products; 152 | sourceTree = ""; 153 | }; 154 | 438257831C1A6F5F00A3DA24 /* CJLabelTest */ = { 155 | isa = PBXGroup; 156 | children = ( 157 | 4396AC9E23E6C3B800836CE2 /* CJLabelTest.entitlements */, 158 | B431D52C1D0545E700228988 /* CJLabel */, 159 | 438257871C1A6F5F00A3DA24 /* AppDelegate.h */, 160 | 438257881C1A6F5F00A3DA24 /* AppDelegate.m */, 161 | B40ACCAB1EB30D8F006DB787 /* Controllers */, 162 | B40ACCAA1EB30D6A006DB787 /* Views */, 163 | 438257901C1A6F5F00A3DA24 /* Assets.xcassets */, 164 | 438257841C1A6F5F00A3DA24 /* Supporting Files */, 165 | ); 166 | path = CJLabelTest; 167 | sourceTree = ""; 168 | }; 169 | 438257841C1A6F5F00A3DA24 /* Supporting Files */ = { 170 | isa = PBXGroup; 171 | children = ( 172 | B4928B9E1FB4003D0017A25C /* Example.json */, 173 | B4928BA51FB447920017A25C /* Common.h */, 174 | 4382578D1C1A6F5F00A3DA24 /* Main.storyboard */, 175 | 438257921C1A6F5F00A3DA24 /* LaunchScreen.storyboard */, 176 | 438257951C1A6F5F00A3DA24 /* Info.plist */, 177 | 438257851C1A6F5F00A3DA24 /* main.m */, 178 | ); 179 | name = "Supporting Files"; 180 | sourceTree = ""; 181 | }; 182 | 4382579D1C1A6F5F00A3DA24 /* CJLabelTestTests */ = { 183 | isa = PBXGroup; 184 | children = ( 185 | 4382579E1C1A6F5F00A3DA24 /* CJLabelTestTests.m */, 186 | 438257A01C1A6F5F00A3DA24 /* Info.plist */, 187 | ); 188 | path = CJLabelTestTests; 189 | sourceTree = ""; 190 | }; 191 | 438257A81C1A6F5F00A3DA24 /* CJLabelTestUITests */ = { 192 | isa = PBXGroup; 193 | children = ( 194 | 438257A91C1A6F5F00A3DA24 /* CJLabelTestUITests.m */, 195 | 438257AB1C1A6F5F00A3DA24 /* Info.plist */, 196 | ); 197 | path = CJLabelTestUITests; 198 | sourceTree = ""; 199 | }; 200 | B40ACCAA1EB30D6A006DB787 /* Views */ = { 201 | isa = PBXGroup; 202 | children = ( 203 | B40ACCA31EB30CF3006DB787 /* AttributedTableViewCell.h */, 204 | B40ACCA41EB30CF3006DB787 /* AttributedTableViewCell.m */, 205 | B40ACCA51EB30CF3006DB787 /* AttributedTableViewCell.xib */, 206 | ); 207 | name = Views; 208 | sourceTree = ""; 209 | }; 210 | B40ACCAB1EB30D8F006DB787 /* Controllers */ = { 211 | isa = PBXGroup; 212 | children = ( 213 | B483EC871FBAC7AD000FCE06 /* FirstDetailViewController.h */, 214 | B483EC861FBAC7AD000FCE06 /* FirstDetailViewController.m */, 215 | B483EC881FBAC7AD000FCE06 /* FirstDetailViewController.xib */, 216 | 4382578A1C1A6F5F00A3DA24 /* ViewController.h */, 217 | 4382578B1C1A6F5F00A3DA24 /* ViewController.m */, 218 | B4C893181EB881EA00AC9E42 /* SecondDetailViewController.h */, 219 | B4C893191EB881EA00AC9E42 /* SecondDetailViewController.m */, 220 | B4C8931A1EB881EA00AC9E42 /* SecondDetailViewController.xib */, 221 | ); 222 | name = Controllers; 223 | path = ../../CJLabel; 224 | sourceTree = ""; 225 | }; 226 | B431D52C1D0545E700228988 /* CJLabel */ = { 227 | isa = PBXGroup; 228 | children = ( 229 | B431D52D1D0545E700228988 /* CJLabel.h */, 230 | B431D52E1D0545E700228988 /* CJLabel.m */, 231 | 43C1652C25C92B1B00EAA38B /* CJLabel+UIMenu.h */, 232 | 43C1652D25C92B1B00EAA38B /* CJLabel+UIMenu.m */, 233 | B45AC7EB1F8A7181006B5336 /* CJLabelConfigure.h */, 234 | B45AC7EC1F8A7181006B5336 /* CJLabelConfigure.m */, 235 | ); 236 | name = CJLabel; 237 | path = ../../CJLabel; 238 | sourceTree = ""; 239 | }; 240 | /* End PBXGroup section */ 241 | 242 | /* Begin PBXNativeTarget section */ 243 | 438257801C1A6F5F00A3DA24 /* CJLabelTest */ = { 244 | isa = PBXNativeTarget; 245 | buildConfigurationList = 438257AE1C1A6F5F00A3DA24 /* Build configuration list for PBXNativeTarget "CJLabelTest" */; 246 | buildPhases = ( 247 | 4382577D1C1A6F5F00A3DA24 /* Sources */, 248 | 4382577E1C1A6F5F00A3DA24 /* Frameworks */, 249 | 4382577F1C1A6F5F00A3DA24 /* Resources */, 250 | E7D4B2B723B2248E00F2E528 /* Embed Frameworks */, 251 | ); 252 | buildRules = ( 253 | ); 254 | dependencies = ( 255 | ); 256 | name = CJLabelTest; 257 | productName = CJLabelTest; 258 | productReference = 438257811C1A6F5F00A3DA24 /* CJLabelTest.app */; 259 | productType = "com.apple.product-type.application"; 260 | }; 261 | 438257991C1A6F5F00A3DA24 /* CJLabelTestTests */ = { 262 | isa = PBXNativeTarget; 263 | buildConfigurationList = 438257B11C1A6F5F00A3DA24 /* Build configuration list for PBXNativeTarget "CJLabelTestTests" */; 264 | buildPhases = ( 265 | 438257961C1A6F5F00A3DA24 /* Sources */, 266 | 438257971C1A6F5F00A3DA24 /* Frameworks */, 267 | 438257981C1A6F5F00A3DA24 /* Resources */, 268 | ); 269 | buildRules = ( 270 | ); 271 | dependencies = ( 272 | 4382579C1C1A6F5F00A3DA24 /* PBXTargetDependency */, 273 | ); 274 | name = CJLabelTestTests; 275 | productName = CJLabelTestTests; 276 | productReference = 4382579A1C1A6F5F00A3DA24 /* CJLabelTestTests.xctest */; 277 | productType = "com.apple.product-type.bundle.unit-test"; 278 | }; 279 | 438257A41C1A6F5F00A3DA24 /* CJLabelTestUITests */ = { 280 | isa = PBXNativeTarget; 281 | buildConfigurationList = 438257B41C1A6F5F00A3DA24 /* Build configuration list for PBXNativeTarget "CJLabelTestUITests" */; 282 | buildPhases = ( 283 | 438257A11C1A6F5F00A3DA24 /* Sources */, 284 | 438257A21C1A6F5F00A3DA24 /* Frameworks */, 285 | 438257A31C1A6F5F00A3DA24 /* Resources */, 286 | ); 287 | buildRules = ( 288 | ); 289 | dependencies = ( 290 | 438257A71C1A6F5F00A3DA24 /* PBXTargetDependency */, 291 | ); 292 | name = CJLabelTestUITests; 293 | productName = CJLabelTestUITests; 294 | productReference = 438257A51C1A6F5F00A3DA24 /* CJLabelTestUITests.xctest */; 295 | productType = "com.apple.product-type.bundle.ui-testing"; 296 | }; 297 | /* End PBXNativeTarget section */ 298 | 299 | /* Begin PBXProject section */ 300 | 438257791C1A6F5F00A3DA24 /* Project object */ = { 301 | isa = PBXProject; 302 | attributes = { 303 | LastUpgradeCheck = 0900; 304 | ORGANIZATIONNAME = C.K.Lian; 305 | TargetAttributes = { 306 | 438257801C1A6F5F00A3DA24 = { 307 | CreatedOnToolsVersion = 7.0; 308 | DevelopmentTeam = 2G9KQAD4K5; 309 | ProvisioningStyle = Automatic; 310 | }; 311 | 438257991C1A6F5F00A3DA24 = { 312 | CreatedOnToolsVersion = 7.0; 313 | TestTargetID = 438257801C1A6F5F00A3DA24; 314 | }; 315 | 438257A41C1A6F5F00A3DA24 = { 316 | CreatedOnToolsVersion = 7.0; 317 | TestTargetID = 438257801C1A6F5F00A3DA24; 318 | }; 319 | }; 320 | }; 321 | buildConfigurationList = 4382577C1C1A6F5F00A3DA24 /* Build configuration list for PBXProject "CJLabelTest" */; 322 | compatibilityVersion = "Xcode 3.2"; 323 | developmentRegion = English; 324 | hasScannedForEncodings = 0; 325 | knownRegions = ( 326 | English, 327 | en, 328 | Base, 329 | ); 330 | mainGroup = 438257781C1A6F5F00A3DA24; 331 | productRefGroup = 438257821C1A6F5F00A3DA24 /* Products */; 332 | projectDirPath = ""; 333 | projectRoot = ""; 334 | targets = ( 335 | 438257801C1A6F5F00A3DA24 /* CJLabelTest */, 336 | 438257991C1A6F5F00A3DA24 /* CJLabelTestTests */, 337 | 438257A41C1A6F5F00A3DA24 /* CJLabelTestUITests */, 338 | ); 339 | }; 340 | /* End PBXProject section */ 341 | 342 | /* Begin PBXResourcesBuildPhase section */ 343 | 4382577F1C1A6F5F00A3DA24 /* Resources */ = { 344 | isa = PBXResourcesBuildPhase; 345 | buildActionMask = 2147483647; 346 | files = ( 347 | B483EC8A1FBAC7AD000FCE06 /* FirstDetailViewController.xib in Resources */, 348 | 438257941C1A6F5F00A3DA24 /* LaunchScreen.storyboard in Resources */, 349 | B4C8931C1EB881EA00AC9E42 /* SecondDetailViewController.xib in Resources */, 350 | 438257911C1A6F5F00A3DA24 /* Assets.xcassets in Resources */, 351 | B4928B9F1FB4003D0017A25C /* Example.json in Resources */, 352 | 4382578F1C1A6F5F00A3DA24 /* Main.storyboard in Resources */, 353 | B40ACCA81EB30CF3006DB787 /* AttributedTableViewCell.xib in Resources */, 354 | ); 355 | runOnlyForDeploymentPostprocessing = 0; 356 | }; 357 | 438257981C1A6F5F00A3DA24 /* Resources */ = { 358 | isa = PBXResourcesBuildPhase; 359 | buildActionMask = 2147483647; 360 | files = ( 361 | ); 362 | runOnlyForDeploymentPostprocessing = 0; 363 | }; 364 | 438257A31C1A6F5F00A3DA24 /* Resources */ = { 365 | isa = PBXResourcesBuildPhase; 366 | buildActionMask = 2147483647; 367 | files = ( 368 | ); 369 | runOnlyForDeploymentPostprocessing = 0; 370 | }; 371 | /* End PBXResourcesBuildPhase section */ 372 | 373 | /* Begin PBXSourcesBuildPhase section */ 374 | 4382577D1C1A6F5F00A3DA24 /* Sources */ = { 375 | isa = PBXSourcesBuildPhase; 376 | buildActionMask = 2147483647; 377 | files = ( 378 | 43C1652E25C92B1B00EAA38B /* CJLabel+UIMenu.m in Sources */, 379 | B483EC891FBAC7AD000FCE06 /* FirstDetailViewController.m in Sources */, 380 | 4382578C1C1A6F5F00A3DA24 /* ViewController.m in Sources */, 381 | B40ACCA71EB30CF3006DB787 /* AttributedTableViewCell.m in Sources */, 382 | B431D5321D0545E700228988 /* CJLabel.m in Sources */, 383 | B4C8931B1EB881EA00AC9E42 /* SecondDetailViewController.m in Sources */, 384 | B45AC7ED1F8A7181006B5336 /* CJLabelConfigure.m in Sources */, 385 | 438257891C1A6F5F00A3DA24 /* AppDelegate.m in Sources */, 386 | 438257861C1A6F5F00A3DA24 /* main.m in Sources */, 387 | ); 388 | runOnlyForDeploymentPostprocessing = 0; 389 | }; 390 | 438257961C1A6F5F00A3DA24 /* Sources */ = { 391 | isa = PBXSourcesBuildPhase; 392 | buildActionMask = 2147483647; 393 | files = ( 394 | 4382579F1C1A6F5F00A3DA24 /* CJLabelTestTests.m in Sources */, 395 | ); 396 | runOnlyForDeploymentPostprocessing = 0; 397 | }; 398 | 438257A11C1A6F5F00A3DA24 /* Sources */ = { 399 | isa = PBXSourcesBuildPhase; 400 | buildActionMask = 2147483647; 401 | files = ( 402 | 438257AA1C1A6F5F00A3DA24 /* CJLabelTestUITests.m in Sources */, 403 | ); 404 | runOnlyForDeploymentPostprocessing = 0; 405 | }; 406 | /* End PBXSourcesBuildPhase section */ 407 | 408 | /* Begin PBXTargetDependency section */ 409 | 4382579C1C1A6F5F00A3DA24 /* PBXTargetDependency */ = { 410 | isa = PBXTargetDependency; 411 | target = 438257801C1A6F5F00A3DA24 /* CJLabelTest */; 412 | targetProxy = 4382579B1C1A6F5F00A3DA24 /* PBXContainerItemProxy */; 413 | }; 414 | 438257A71C1A6F5F00A3DA24 /* PBXTargetDependency */ = { 415 | isa = PBXTargetDependency; 416 | target = 438257801C1A6F5F00A3DA24 /* CJLabelTest */; 417 | targetProxy = 438257A61C1A6F5F00A3DA24 /* PBXContainerItemProxy */; 418 | }; 419 | /* End PBXTargetDependency section */ 420 | 421 | /* Begin PBXVariantGroup section */ 422 | 4382578D1C1A6F5F00A3DA24 /* Main.storyboard */ = { 423 | isa = PBXVariantGroup; 424 | children = ( 425 | 4382578E1C1A6F5F00A3DA24 /* Base */, 426 | ); 427 | name = Main.storyboard; 428 | sourceTree = ""; 429 | }; 430 | 438257921C1A6F5F00A3DA24 /* LaunchScreen.storyboard */ = { 431 | isa = PBXVariantGroup; 432 | children = ( 433 | 438257931C1A6F5F00A3DA24 /* Base */, 434 | ); 435 | name = LaunchScreen.storyboard; 436 | sourceTree = ""; 437 | }; 438 | /* End PBXVariantGroup section */ 439 | 440 | /* Begin XCBuildConfiguration section */ 441 | 438257AC1C1A6F5F00A3DA24 /* Debug */ = { 442 | isa = XCBuildConfiguration; 443 | buildSettings = { 444 | ALWAYS_SEARCH_USER_PATHS = NO; 445 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 446 | CLANG_CXX_LIBRARY = "libc++"; 447 | CLANG_ENABLE_MODULES = YES; 448 | CLANG_ENABLE_OBJC_ARC = YES; 449 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 450 | CLANG_WARN_BOOL_CONVERSION = YES; 451 | CLANG_WARN_COMMA = YES; 452 | CLANG_WARN_CONSTANT_CONVERSION = YES; 453 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 454 | CLANG_WARN_EMPTY_BODY = YES; 455 | CLANG_WARN_ENUM_CONVERSION = YES; 456 | CLANG_WARN_INFINITE_RECURSION = YES; 457 | CLANG_WARN_INT_CONVERSION = YES; 458 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 459 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 460 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 461 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 462 | CLANG_WARN_STRICT_PROTOTYPES = YES; 463 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 464 | CLANG_WARN_UNREACHABLE_CODE = YES; 465 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 466 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 467 | COPY_PHASE_STRIP = NO; 468 | DEBUG_INFORMATION_FORMAT = dwarf; 469 | ENABLE_STRICT_OBJC_MSGSEND = YES; 470 | ENABLE_TESTABILITY = YES; 471 | GCC_C_LANGUAGE_STANDARD = gnu99; 472 | GCC_DYNAMIC_NO_PIC = NO; 473 | GCC_NO_COMMON_BLOCKS = YES; 474 | GCC_OPTIMIZATION_LEVEL = 0; 475 | GCC_PREPROCESSOR_DEFINITIONS = ( 476 | "DEBUG=1", 477 | "$(inherited)", 478 | ); 479 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 480 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 481 | GCC_WARN_UNDECLARED_SELECTOR = YES; 482 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 483 | GCC_WARN_UNUSED_FUNCTION = YES; 484 | GCC_WARN_UNUSED_VARIABLE = YES; 485 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 486 | MTL_ENABLE_DEBUG_INFO = YES; 487 | ONLY_ACTIVE_ARCH = YES; 488 | SDKROOT = iphoneos; 489 | TARGETED_DEVICE_FAMILY = "1,2"; 490 | }; 491 | name = Debug; 492 | }; 493 | 438257AD1C1A6F5F00A3DA24 /* Release */ = { 494 | isa = XCBuildConfiguration; 495 | buildSettings = { 496 | ALWAYS_SEARCH_USER_PATHS = NO; 497 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 498 | CLANG_CXX_LIBRARY = "libc++"; 499 | CLANG_ENABLE_MODULES = YES; 500 | CLANG_ENABLE_OBJC_ARC = YES; 501 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 502 | CLANG_WARN_BOOL_CONVERSION = YES; 503 | CLANG_WARN_COMMA = YES; 504 | CLANG_WARN_CONSTANT_CONVERSION = YES; 505 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 506 | CLANG_WARN_EMPTY_BODY = YES; 507 | CLANG_WARN_ENUM_CONVERSION = YES; 508 | CLANG_WARN_INFINITE_RECURSION = YES; 509 | CLANG_WARN_INT_CONVERSION = YES; 510 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 511 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 512 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 513 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 514 | CLANG_WARN_STRICT_PROTOTYPES = YES; 515 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 516 | CLANG_WARN_UNREACHABLE_CODE = YES; 517 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 518 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 519 | COPY_PHASE_STRIP = NO; 520 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 521 | ENABLE_NS_ASSERTIONS = NO; 522 | ENABLE_STRICT_OBJC_MSGSEND = YES; 523 | GCC_C_LANGUAGE_STANDARD = gnu99; 524 | GCC_NO_COMMON_BLOCKS = YES; 525 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 526 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 527 | GCC_WARN_UNDECLARED_SELECTOR = YES; 528 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 529 | GCC_WARN_UNUSED_FUNCTION = YES; 530 | GCC_WARN_UNUSED_VARIABLE = YES; 531 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 532 | MTL_ENABLE_DEBUG_INFO = NO; 533 | SDKROOT = iphoneos; 534 | TARGETED_DEVICE_FAMILY = "1,2"; 535 | VALIDATE_PRODUCT = YES; 536 | }; 537 | name = Release; 538 | }; 539 | 438257AF1C1A6F5F00A3DA24 /* Debug */ = { 540 | isa = XCBuildConfiguration; 541 | buildSettings = { 542 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 543 | CODE_SIGN_ENTITLEMENTS = CJLabelTest/CJLabelTest.entitlements; 544 | CODE_SIGN_IDENTITY = "Apple Development"; 545 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 546 | CODE_SIGN_STYLE = Automatic; 547 | DEVELOPMENT_TEAM = 2G9KQAD4K5; 548 | FRAMEWORK_SEARCH_PATHS = ( 549 | "$(inherited)", 550 | "$(PROJECT_DIR)", 551 | "$(PROJECT_DIR)/CJLabelTest", 552 | ); 553 | INFOPLIST_FILE = CJLabelTest/Info.plist; 554 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 555 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 556 | OTHER_LDFLAGS = ""; 557 | PRODUCT_BUNDLE_IDENTIFIER = Jin.Lian.Test; 558 | PRODUCT_NAME = "$(TARGET_NAME)"; 559 | PROVISIONING_PROFILE_SPECIFIER = ""; 560 | }; 561 | name = Debug; 562 | }; 563 | 438257B01C1A6F5F00A3DA24 /* Release */ = { 564 | isa = XCBuildConfiguration; 565 | buildSettings = { 566 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 567 | CODE_SIGN_ENTITLEMENTS = CJLabelTest/CJLabelTest.entitlements; 568 | CODE_SIGN_IDENTITY = "Apple Development"; 569 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 570 | CODE_SIGN_STYLE = Automatic; 571 | DEVELOPMENT_TEAM = 2G9KQAD4K5; 572 | FRAMEWORK_SEARCH_PATHS = ( 573 | "$(inherited)", 574 | "$(PROJECT_DIR)", 575 | "$(PROJECT_DIR)/CJLabelTest", 576 | ); 577 | INFOPLIST_FILE = CJLabelTest/Info.plist; 578 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 579 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 580 | OTHER_LDFLAGS = ""; 581 | PRODUCT_BUNDLE_IDENTIFIER = Jin.Lian.Test; 582 | PRODUCT_NAME = "$(TARGET_NAME)"; 583 | PROVISIONING_PROFILE_SPECIFIER = ""; 584 | }; 585 | name = Release; 586 | }; 587 | 438257B21C1A6F5F00A3DA24 /* Debug */ = { 588 | isa = XCBuildConfiguration; 589 | buildSettings = { 590 | BUNDLE_LOADER = "$(TEST_HOST)"; 591 | INFOPLIST_FILE = CJLabelTestTests/Info.plist; 592 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 593 | PRODUCT_BUNDLE_IDENTIFIER = cj.CJLabelTestTests; 594 | PRODUCT_NAME = "$(TARGET_NAME)"; 595 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/CJLabelTest.app/CJLabelTest"; 596 | }; 597 | name = Debug; 598 | }; 599 | 438257B31C1A6F5F00A3DA24 /* Release */ = { 600 | isa = XCBuildConfiguration; 601 | buildSettings = { 602 | BUNDLE_LOADER = "$(TEST_HOST)"; 603 | INFOPLIST_FILE = CJLabelTestTests/Info.plist; 604 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 605 | PRODUCT_BUNDLE_IDENTIFIER = cj.CJLabelTestTests; 606 | PRODUCT_NAME = "$(TARGET_NAME)"; 607 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/CJLabelTest.app/CJLabelTest"; 608 | }; 609 | name = Release; 610 | }; 611 | 438257B51C1A6F5F00A3DA24 /* Debug */ = { 612 | isa = XCBuildConfiguration; 613 | buildSettings = { 614 | INFOPLIST_FILE = CJLabelTestUITests/Info.plist; 615 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 616 | PRODUCT_BUNDLE_IDENTIFIER = cj.CJLabelTestUITests; 617 | PRODUCT_NAME = "$(TARGET_NAME)"; 618 | TEST_TARGET_NAME = CJLabelTest; 619 | USES_XCTRUNNER = YES; 620 | }; 621 | name = Debug; 622 | }; 623 | 438257B61C1A6F5F00A3DA24 /* Release */ = { 624 | isa = XCBuildConfiguration; 625 | buildSettings = { 626 | INFOPLIST_FILE = CJLabelTestUITests/Info.plist; 627 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 628 | PRODUCT_BUNDLE_IDENTIFIER = cj.CJLabelTestUITests; 629 | PRODUCT_NAME = "$(TARGET_NAME)"; 630 | TEST_TARGET_NAME = CJLabelTest; 631 | USES_XCTRUNNER = YES; 632 | }; 633 | name = Release; 634 | }; 635 | /* End XCBuildConfiguration section */ 636 | 637 | /* Begin XCConfigurationList section */ 638 | 4382577C1C1A6F5F00A3DA24 /* Build configuration list for PBXProject "CJLabelTest" */ = { 639 | isa = XCConfigurationList; 640 | buildConfigurations = ( 641 | 438257AC1C1A6F5F00A3DA24 /* Debug */, 642 | 438257AD1C1A6F5F00A3DA24 /* Release */, 643 | ); 644 | defaultConfigurationIsVisible = 0; 645 | defaultConfigurationName = Release; 646 | }; 647 | 438257AE1C1A6F5F00A3DA24 /* Build configuration list for PBXNativeTarget "CJLabelTest" */ = { 648 | isa = XCConfigurationList; 649 | buildConfigurations = ( 650 | 438257AF1C1A6F5F00A3DA24 /* Debug */, 651 | 438257B01C1A6F5F00A3DA24 /* Release */, 652 | ); 653 | defaultConfigurationIsVisible = 0; 654 | defaultConfigurationName = Release; 655 | }; 656 | 438257B11C1A6F5F00A3DA24 /* Build configuration list for PBXNativeTarget "CJLabelTestTests" */ = { 657 | isa = XCConfigurationList; 658 | buildConfigurations = ( 659 | 438257B21C1A6F5F00A3DA24 /* Debug */, 660 | 438257B31C1A6F5F00A3DA24 /* Release */, 661 | ); 662 | defaultConfigurationIsVisible = 0; 663 | defaultConfigurationName = Release; 664 | }; 665 | 438257B41C1A6F5F00A3DA24 /* Build configuration list for PBXNativeTarget "CJLabelTestUITests" */ = { 666 | isa = XCConfigurationList; 667 | buildConfigurations = ( 668 | 438257B51C1A6F5F00A3DA24 /* Debug */, 669 | 438257B61C1A6F5F00A3DA24 /* Release */, 670 | ); 671 | defaultConfigurationIsVisible = 0; 672 | defaultConfigurationName = Release; 673 | }; 674 | /* End XCConfigurationList section */ 675 | }; 676 | rootObject = 438257791C1A6F5F00A3DA24 /* Project object */; 677 | } 678 | -------------------------------------------------------------------------------- /Example/CJLabelTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/CJLabelTest.xcodeproj/xcuserdata/CK.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 9 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /Example/CJLabelTest.xcodeproj/xcuserdata/CK.xcuserdatad/xcschemes/CJLabelTest.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 43 | 49 | 50 | 51 | 52 | 53 | 59 | 60 | 61 | 62 | 63 | 64 | 74 | 76 | 82 | 83 | 84 | 85 | 86 | 87 | 93 | 95 | 101 | 102 | 103 | 104 | 106 | 107 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /Example/CJLabelTest.xcodeproj/xcuserdata/CK.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | CJLabelTest.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 438257801C1A6F5F00A3DA24 16 | 17 | primary 18 | 19 | 20 | 438257991C1A6F5F00A3DA24 21 | 22 | primary 23 | 24 | 25 | 438257A41C1A6F5F00A3DA24 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /Example/CJLabelTest.xcodeproj/xcuserdata/Y.C.Lian.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 14 | 15 | 16 | 18 | 30 | 31 | 32 | 34 | 46 | 47 | 48 | 50 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /Example/CJLabelTest.xcodeproj/xcuserdata/Y.C.Lian.xcuserdatad/xcschemes/CJLabelTest.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 34 | 40 | 41 | 42 | 44 | 50 | 51 | 52 | 53 | 54 | 60 | 61 | 62 | 63 | 64 | 65 | 76 | 78 | 84 | 85 | 86 | 87 | 91 | 92 | 96 | 97 | 101 | 102 | 103 | 104 | 105 | 106 | 112 | 114 | 120 | 121 | 122 | 123 | 125 | 126 | 129 | 130 | 131 | -------------------------------------------------------------------------------- /Example/CJLabelTest.xcodeproj/xcuserdata/Y.C.Lian.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | CJLabelTest.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 438257801C1A6F5F00A3DA24 16 | 17 | primary 18 | 19 | 20 | 438257991C1A6F5F00A3DA24 21 | 22 | primary 23 | 24 | 25 | 438257A41C1A6F5F00A3DA24 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /Example/CJLabelTest/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // CJLabelTest 4 | // 5 | // Created by C.K.Lian on 15/12/11. 6 | // Copyright © 2015年 C.K.Lian. 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 | -------------------------------------------------------------------------------- /Example/CJLabelTest/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // CJLabelTest 4 | // 5 | // Created by C.K.Lian on 15/12/11. 6 | // Copyright © 2015年 C.K.Lian. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | - (void)applicationWillResignActive:(UIApplication *)application { 24 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 25 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 26 | } 27 | 28 | - (void)applicationDidEnterBackground:(UIApplication *)application { 29 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 31 | } 32 | 33 | - (void)applicationWillEnterForeground:(UIApplication *)application { 34 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 35 | } 36 | 37 | - (void)applicationDidBecomeActive:(UIApplication *)application { 38 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 39 | } 40 | 41 | - (void)applicationWillTerminate:(UIApplication *)application { 42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /Example/CJLabelTest/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 | } -------------------------------------------------------------------------------- /Example/CJLabelTest/Assets.xcassets/CJLabel.imageset/CJLabel@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lele8446/CJLabel/da4862371768c71d33af705fb892f2ffda7b3b1b/Example/CJLabelTest/Assets.xcassets/CJLabel.imageset/CJLabel@2x.png -------------------------------------------------------------------------------- /Example/CJLabelTest/Assets.xcassets/CJLabel.imageset/CJLabel@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lele8446/CJLabel/da4862371768c71d33af705fb892f2ffda7b3b1b/Example/CJLabelTest/Assets.xcassets/CJLabel.imageset/CJLabel@3x.png -------------------------------------------------------------------------------- /Example/CJLabelTest/Assets.xcassets/CJLabel.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "CJLabel@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "CJLabel@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /Example/CJLabelTest/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Example/CJLabelTest/Assets.xcassets/backImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "backImage@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "backImage@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /Example/CJLabelTest/Assets.xcassets/backImage.imageset/backImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lele8446/CJLabel/da4862371768c71d33af705fb892f2ffda7b3b1b/Example/CJLabelTest/Assets.xcassets/backImage.imageset/backImage@2x.png -------------------------------------------------------------------------------- /Example/CJLabelTest/Assets.xcassets/backImage.imageset/backImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lele8446/CJLabel/da4862371768c71d33af705fb892f2ffda7b3b1b/Example/CJLabelTest/Assets.xcassets/backImage.imageset/backImage@3x.png -------------------------------------------------------------------------------- /Example/CJLabelTest/AttributedTableViewCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // AttributedTableViewCell.h 3 | // tableViewLabel 4 | // 5 | // Created by YiChe on 16/4/19. 6 | // Copyright © 2016年 YiChe. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "CJLabel.h" 11 | 12 | @interface AttributedTableViewCell : UITableViewCell 13 | @property (nonatomic,weak)IBOutlet CJLabel *label; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Example/CJLabelTest/AttributedTableViewCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // AttributedTableViewCell.m 3 | // tableViewLabel 4 | // 5 | // Created by YiChe on 16/4/19. 6 | // Copyright © 2016年 YiChe. All rights reserved. 7 | // 8 | 9 | #import "AttributedTableViewCell.h" 10 | 11 | @implementation AttributedTableViewCell 12 | 13 | - (void)awakeFromNib { 14 | [super awakeFromNib]; 15 | self.label.preferredMaxLayoutWidth = [[UIScreen mainScreen] bounds].size.width - 20; 16 | self.label.numberOfLines = 4; 17 | self.label.textInsets = UIEdgeInsetsMake(5, 5, 5, 0); 18 | self.label.enableCopy = YES; 19 | // self.label.font = [UIFont systemFontOfSize:12]; 20 | // self.label.attributedTruncationToken = [[NSAttributedString alloc]initWithString:@""]; 21 | } 22 | 23 | - (void)setSelected:(BOOL)selected animated:(BOOL)animated { 24 | [super setSelected:selected animated:animated]; 25 | 26 | // Configure the view for the selected state 27 | } 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /Example/CJLabelTest/AttributedTableViewCell.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /Example/CJLabelTest/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Example/CJLabelTest/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /Example/CJLabelTest/CJLabelTest.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Example/CJLabelTest/Common.h: -------------------------------------------------------------------------------- 1 | // 2 | // Common.h 3 | // CJLabelTest 4 | // 5 | // Created by ChiJinLian on 2017/11/9. 6 | // Copyright © 2017年 C.K.Lian. All rights reserved. 7 | // 8 | 9 | #ifndef Common_h 10 | #define Common_h 11 | 12 | #define ScreenWidth [[UIScreen mainScreen] bounds].size.width 13 | 14 | #define ScreenHeight [[UIScreen mainScreen] bounds].size.height 15 | 16 | #define UIRGBColor(r,g,b,a) ([UIColor colorWithRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:a]) 17 | #define UIColorFromRGB(rgbValue) [UIColor colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 \ 18 | green:((float)((rgbValue & 0xFF00) >> 8))/255.0 \ 19 | blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0] 20 | 21 | #endif /* Common_h */ 22 | -------------------------------------------------------------------------------- /Example/CJLabelTest/Example.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "text": "示例一:富文本展示\n\n`CJLabel`支持富文本展示:包括设置不同字体💯,插入图片设置字体背景色、字体边框线、边框线圆角、对指定文本添加删除线;另外设置文本内边距为{10,10,10,0}。" 4 | }, 5 | { 6 | "text": "示例二:垂直对齐\n\n`CJLabel`设置整体文本垂直居中,🐹点击可调整文本在垂直方向的对齐方式。\n支持复制,双击或者长按可唤起😁UIMenuController进行选择复制文本操作。" 7 | }, 8 | { 9 | "text": "示例三:选择复制\n\n支持复制,😆双击或者长按可唤起😁UIMenuController进行选择复制文本操作。\n设置`CJLabel`为可点击链点,并指定其字体大小粗体15,字体颜色蓝色,边框线颜色为橙黄色,边框线粗细为1,边框线圆角取默认值5,背景填充颜色为浅灰色;👻点击高亮时字体颜色红色,边框线为红色,点击背景色橘黄色👏。" 10 | }, 11 | { 12 | "text": "示例四:点击事件\n\n设置多段重复内容都为可点击链点😊,第一个`CJLabel`响应点击和长按事件,第二个`CJLabel`只响应点击事件,👀第三个`CJLabel`只响应长按事件并且与其他链点的样式不相同;整体文本在垂直方向为底部对齐。" 13 | }, 14 | { 15 | "text": "示例五:图文混排\n\n😛`CJLabel`在示例文本中插入图片 😁设置图片边框颜色为红色,边框线粗细取默认值1,并设置图片为可点击链点,同时可调整图片所在行在垂直方向的对齐方式。" 16 | }, 17 | { 18 | "text": "示例六:自定义截断字符`TruncationToken`为:“……全文”,默认字体颜色为黑色,最多显示3行,超出3行部分自动截断,截断方式为行尾截断,截断字符以蓝色高亮显示,点击展开所有内容。支持复制,双击或者长按可唤起UIMenuController进行选择复制文本操作。 #CJLabel# 继承自 UILabel,支持富文本展示、图文混排、添加自定义点击链点以及选择复制等功能。" 19 | }, 20 | { 21 | "text": "示例七:不可换行标签,#CJLabel#,#CJLabel#,标签#CJLabel#,又一个标签#CJLabel#,#CJLabel#,#CJLabel#,#CJLabel#" 22 | }, 23 | ] 24 | -------------------------------------------------------------------------------- /Example/CJLabelTest/FirstDetailViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // SecondDetailViewController.h 3 | // CJLabelTest 4 | // 5 | // Created by ChiJinLian on 2017/11/9. 6 | // Copyright © 2017年 C.K.Lian. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface FirstDetailViewController : UIViewController 12 | 13 | @property (nonatomic, assign) NSInteger index; 14 | @property (nonatomic, strong) NSMutableAttributedString *content; 15 | @end 16 | -------------------------------------------------------------------------------- /Example/CJLabelTest/FirstDetailViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // SecondDetailViewController.m 3 | // CJLabelTest 4 | // 5 | // Created by ChiJinLian on 2017/11/9. 6 | // Copyright © 2017年 C.K.Lian. All rights reserved. 7 | // 8 | 9 | #import "FirstDetailViewController.h" 10 | #import "Common.h" 11 | #import "CJLabel.h" 12 | #import "CJLabel+UIMenu.h" 13 | 14 | @interface FirstDetailViewController () 15 | @property (nonatomic, strong) CJLabel *label; 16 | @property (nonatomic, strong) NSAttributedString *attStr; 17 | @end 18 | 19 | @implementation FirstDetailViewController 20 | 21 | - (void)dealloc { 22 | NSLog(@"%@ dealloc",NSStringFromClass([self class])); 23 | } 24 | 25 | - (void)viewDidLoad { 26 | [super viewDidLoad]; 27 | self.edgesForExtendedLayout = UIRectEdgeNone; 28 | self.automaticallyAdjustsScrollViewInsets = NO; 29 | 30 | self.label = [[CJLabel alloc]initWithFrame:CGRectMake(10, 10, ScreenWidth - 20, ScreenHeight - 64 - 150)]; 31 | self.label.backgroundColor = UIColorFromRGB(0XE3F6FF); 32 | self.label.numberOfLines = 0; 33 | [self.view addSubview:self.label]; 34 | 35 | [self handleContent:self.content]; 36 | } 37 | 38 | - (void)didReceiveMemoryWarning { 39 | [super didReceiveMemoryWarning]; 40 | // Dispose of any resources that can be recreated. 41 | } 42 | 43 | - (void)handleContent:(NSMutableAttributedString *)content { 44 | 45 | NSMutableAttributedString *attStr = content; 46 | 47 | CJLabelConfigure *configure = [CJLabel configureAttributes:nil isLink:NO activeLinkAttributes:nil parameter:nil clickLinkBlock:nil longPressBlock:nil]; 48 | configure.isLink = NO; 49 | 50 | attStr = [CJLabel configureAttrString:attStr withString:@"CJLabel" sameStringEnable:YES configure:configure]; 51 | self.attStr = attStr; 52 | 53 | __weak typeof(self)wSelf = self; 54 | switch (self.index) { 55 | case 1: 56 | self.navigationItem.title = @"垂直对齐"; 57 | //设置垂直对齐方式 58 | self.label.verticalAlignment = CJVerticalAlignmentCenter; 59 | self.label.text = self.attStr; 60 | //支持选择复制 61 | self.label.enableCopy = YES; 62 | [self rightBarButtonItems]; 63 | break; 64 | case 3: 65 | { 66 | self.navigationItem.title = @"添加不同链点"; 67 | self.label.verticalAlignment = CJVerticalAlignmentBottom; 68 | 69 | NSArray *linkRangeArray = [CJLabel sameLinkStringRangeArray:@"CJLabel" inAttString:attStr]; 70 | //第一个链点 71 | NSRange linkRange = [linkRangeArray[0] rangeValue]; 72 | configure.attributes = @{ 73 | NSForegroundColorAttributeName:[UIColor blueColor], 74 | NSFontAttributeName:[UIFont boldSystemFontOfSize:15], 75 | kCJBackgroundStrokeColorAttributeName:[UIColor orangeColor], 76 | kCJBackgroundFillColorAttributeName:[UIColor lightGrayColor] 77 | }; 78 | configure.activeLinkAttributes = @{ 79 | NSForegroundColorAttributeName:[UIColor redColor], 80 | kCJActiveBackgroundStrokeColorAttributeName:[UIColor redColor], 81 | kCJActiveBackgroundFillColorAttributeName:UIRGBColor(247,231,121,1) 82 | }; 83 | configure.isLink = YES; 84 | configure.clickLinkBlock = ^(CJLabelLinkModel *linkModel) { 85 | [wSelf clickLink:linkModel]; 86 | }; 87 | configure.longPressBlock = ^(CJLabelLinkModel *linkModel) { 88 | [wSelf clicklongPressLink:linkModel]; 89 | }; 90 | configure.parameter = @"第1个点击链点"; 91 | attStr = [CJLabel configureAttrString:attStr atRange:linkRange configure:configure]; 92 | 93 | //第二个点击链点 94 | linkRange = [linkRangeArray[1] rangeValue]; 95 | configure.longPressBlock = nil; 96 | configure.parameter = @"第2个点击链点"; 97 | attStr = [CJLabel configureAttrString:attStr atRange:linkRange configure:configure]; 98 | 99 | //第三个点击链点 100 | linkRange = [linkRangeArray[2] rangeValue]; 101 | configure.attributes = @{NSForegroundColorAttributeName:[UIColor redColor], 102 | NSFontAttributeName:[UIFont systemFontOfSize:15], 103 | kCJBackgroundStrokeColorAttributeName:[UIColor redColor], 104 | kCJBackgroundFillColorAttributeName:[UIColor colorWithWhite:0.7 alpha:0.7] 105 | }; 106 | configure.activeLinkAttributes = @{NSForegroundColorAttributeName:[UIColor darkTextColor], 107 | kCJActiveBackgroundStrokeColorAttributeName:[UIColor darkTextColor], 108 | kCJActiveBackgroundFillColorAttributeName:[UIColor lightGrayColor] 109 | }; 110 | configure.clickLinkBlock = nil; 111 | configure.parameter = @"第3个点击链点"; 112 | configure.longPressBlock = ^(CJLabelLinkModel *linkModel) { 113 | [wSelf clicklongPressLink:linkModel]; 114 | }; 115 | attStr = [CJLabel configureAttrString:attStr atRange:linkRange configure:configure]; 116 | 117 | self.label.attributedText = attStr; 118 | [self.label showSelectAllTextWithMenus:@[@"拷贝",@"翻译",@"搜一搜"] selectTextBackColor:[UIColor lightGrayColor] colorAlpha:0.35 clickMenuCompletion:^(NSString *menuTitle, CJLabel *label) { 119 | NSLog(@"点击 = %@",menuTitle); 120 | }]; 121 | [CJLabel respondsToSelector:@selector(initWithView:viewSize:lineAlignment:configure:)]; 122 | [CJLabel respondsToSelector:@selector(insertViewAtAttrString:view:viewSize:atIndex:lineAlignment:configure:)]; 123 | } 124 | break; 125 | case 5: 126 | { 127 | self.navigationItem.title = @"自定义截断字符"; 128 | self.label.numberOfLines = 3; 129 | 130 | //配置链点属性 131 | configure.isLink = YES; 132 | configure.clickLinkBlock = ^(CJLabelLinkModel *linkModel) { 133 | //点击 `……全文` 134 | [wSelf clickTruncationToken:linkModel]; 135 | }; 136 | configure.attributes = @{NSForegroundColorAttributeName:[UIColor blueColor],NSFontAttributeName:[UIFont systemFontOfSize:13]}; 137 | 138 | //自定义截断字符为:"……全文" 139 | NSAttributedString *truncationToken = [CJLabel initWithAttributedString:[[NSAttributedString alloc]initWithString:@"……全文"] strIdentifier:@"TruncationToken" configure:configure]; 140 | //设置行尾截断 141 | self.label.lineBreakMode = NSLineBreakByTruncatingTail; 142 | self.label.attributedTruncationToken = truncationToken; 143 | //设置点击链点 144 | attStr = [CJLabel configureAttrString:attStr withAttributedString:truncationToken strIdentifier:@"TruncationToken" sameStringEnable:NO configure:configure]; 145 | 146 | UIImageView *imgView = [[UIImageView alloc]initWithFrame:CGRectMake(0, 0, 100, 80)]; 147 | NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1576752448623&di=42bd4f5d4a9f8f35eb8eade57484fde0&imgtype=0&src=http%3A%2F%2Faliyunzixunbucket.oss-cn-beijing.aliyuncs.com%2Fjpg%2Fb651212dd7b9019275ccc0266f7919ad.jpg%3Fx-oss-process%3Dimage%2Fresize%2Cp_100%2Fauto-orient%2C1%2Fquality%2Cq_90%2Fformat%2Cjpg%2Fwatermark%2Cimage_eXVuY2VzaGk%3D%2Ct_100"]]; 148 | imgView.image = [UIImage imageWithData:data]; 149 | attStr = [CJLabel insertViewAtAttrString:attStr view:imgView viewSize:CGSizeMake(100, 80) atIndex:attStr.length-12 lineAlignment:CJVerticalAlignmentBottom configure:nil]; 150 | 151 | UISwitch *mySwitch = [[UISwitch alloc]initWithFrame:CGRectMake(40, 10, 50, 31)]; 152 | mySwitch.on = YES; 153 | attStr = [CJLabel insertViewAtAttrString:attStr view:mySwitch viewSize:CGSizeMake(50, 31) atIndex:attStr.length-6 lineAlignment:CJVerticalAlignmentBottom configure:nil]; 154 | 155 | self.label.attributedText = attStr; 156 | //支持选择复制 157 | // self.label.enableCopy = YES; 158 | CGFloat height = [CJLabel sizeWithAttributedString:attStr withConstraints:CGSizeMake(ScreenWidth-20, CGFLOAT_MAX) limitedToNumberOfLines:3].height; 159 | self.label.frame = CGRectMake(10, 10, ScreenWidth - 20, height); 160 | } 161 | default: 162 | break; 163 | } 164 | } 165 | 166 | - (void)clickTruncationToken:(CJLabelLinkModel *)linkModel { 167 | NSLog(@"点击了 `……全文`"); 168 | [self truncationTokenRightBarButtonItem:YES]; 169 | NSAttributedString *text = linkModel.label.attributedText; 170 | linkModel.label.numberOfLines = 0; 171 | CGFloat height = [CJLabel sizeWithAttributedString:text withConstraints:CGSizeMake(ScreenWidth-20, CGFLOAT_MAX) limitedToNumberOfLines:0].height; 172 | linkModel.label.frame = CGRectMake(10, 10, ScreenWidth - 20, height); 173 | [linkModel.label flushText]; 174 | } 175 | 176 | - (void)truncationTokenRightBarButtonItem:(BOOL)show { 177 | if (show) { 178 | UIBarButtonItem *item = [[UIBarButtonItem alloc]initWithTitle:@"收起" style:UIBarButtonItemStylePlain target:self action:@selector(itemClick:)]; 179 | item.tag = 400; 180 | self.navigationItem.rightBarButtonItem = item; 181 | }else{ 182 | self.navigationItem.rightBarButtonItem = nil; 183 | } 184 | } 185 | 186 | - (void)rightBarButtonItems { 187 | UIBarButtonItem *item1 = [[UIBarButtonItem alloc]initWithTitle:@"居上" style:UIBarButtonItemStylePlain target:self action:@selector(itemClick:)]; 188 | item1.tag = 100; 189 | UIBarButtonItem *item2 = [[UIBarButtonItem alloc]initWithTitle:@"居中" style:UIBarButtonItemStylePlain target:self action:@selector(itemClick:)]; 190 | item2.tag = 200; 191 | UIBarButtonItem *item3 = [[UIBarButtonItem alloc]initWithTitle:@"居下" style:UIBarButtonItemStylePlain target:self action:@selector(itemClick:)]; 192 | item3.tag = 300; 193 | self.navigationItem.rightBarButtonItems = @[item3,item2,item1]; 194 | } 195 | 196 | - (void)itemClick:(UIBarButtonItem *)item { 197 | 198 | if (item.tag == 100) { 199 | self.label.verticalAlignment = CJVerticalAlignmentTop; 200 | }else if (item.tag == 200) { 201 | self.label.verticalAlignment = CJVerticalAlignmentCenter; 202 | }else if (item.tag == 300) { 203 | self.label.verticalAlignment = CJVerticalAlignmentBottom; 204 | }else if (item.tag == 400) { 205 | self.label.numberOfLines = 3; 206 | CGFloat height = [CJLabel sizeWithAttributedString:self.label.attributedText withConstraints:CGSizeMake(ScreenWidth-20, CGFLOAT_MAX) limitedToNumberOfLines:3].height; 207 | self.label.frame = CGRectMake(10, 10, ScreenWidth - 20, height); 208 | [self.label flushText]; 209 | [self truncationTokenRightBarButtonItem:NO]; 210 | } 211 | 212 | } 213 | 214 | - (void)clickLink:(CJLabelLinkModel *)linkModel { 215 | NSString *title = [NSString stringWithFormat:@"点击链点 %@",linkModel.attributedString.string]; 216 | NSString *parameter = [NSString stringWithFormat:@"自定义参数:%@",linkModel.parameter]; 217 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:title message:parameter preferredStyle:UIAlertControllerStyleAlert]; 218 | UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil]; 219 | [alert addAction:cancel]; 220 | [self presentViewController:alert animated:YES completion:nil]; 221 | } 222 | 223 | - (void)clicklongPressLink:(CJLabelLinkModel *)linkModel { 224 | NSString *title = [NSString stringWithFormat:@"长按点击: %@",linkModel.parameter]; 225 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:title message:nil preferredStyle:UIAlertControllerStyleActionSheet]; 226 | UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil]; 227 | [alert addAction:cancel]; 228 | [self presentViewController:alert animated:YES completion:nil]; 229 | 230 | } 231 | 232 | @end 233 | -------------------------------------------------------------------------------- /Example/CJLabelTest/FirstDetailViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Example/CJLabelTest/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | zh_CN 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /Example/CJLabelTest/SecondDetailViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // DetailViewController.h 3 | // CJLabelTest 4 | // 5 | // Created by ChiJinLian on 2017/4/28. 6 | // Copyright © 2017年 C.K.Lian. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SecondDetailViewController : UIViewController 12 | 13 | @property (nonatomic, assign) NSInteger index; 14 | @property (nonatomic, strong) NSMutableAttributedString *content; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /Example/CJLabelTest/SecondDetailViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // DetailViewController.m 3 | // CJLabelTest 4 | // 5 | // Created by ChiJinLian on 2017/4/28. 6 | // Copyright © 2017年 C.K.Lian. All rights reserved. 7 | // 8 | 9 | #import "SecondDetailViewController.h" 10 | #import "CJLabel.h" 11 | #import "Common.h" 12 | 13 | 14 | @interface SecondDetailViewController () 15 | @property (nonatomic, weak) IBOutlet CJLabel *label; 16 | @property (nonatomic, strong) CJLabel *secondLabel; 17 | @property (nonatomic, strong) NSMutableAttributedString *attStr; 18 | @property (nonatomic, strong) CJLabelConfigure *configure; 19 | 20 | @property (nonatomic, weak) IBOutlet UILabel *label2; 21 | @end 22 | 23 | @implementation SecondDetailViewController 24 | 25 | - (void)dealloc { 26 | NSLog(@"%@ dealloc",NSStringFromClass([self class])); 27 | } 28 | 29 | - (void)viewDidLoad { 30 | [super viewDidLoad]; 31 | self.edgesForExtendedLayout = UIRectEdgeNone; 32 | self.automaticallyAdjustsScrollViewInsets = NO; 33 | 34 | self.label.numberOfLines = 0; 35 | 36 | [self handleContent:self.content]; 37 | 38 | } 39 | 40 | - (void)didReceiveMemoryWarning { 41 | [super didReceiveMemoryWarning]; 42 | } 43 | 44 | - (void)handleContent:(NSMutableAttributedString *)content { 45 | NSMutableAttributedString *attStr = content; 46 | 47 | CJLabelConfigure *configure = [CJLabel configureAttributes:nil isLink:NO activeLinkAttributes:nil parameter:nil clickLinkBlock:nil longPressBlock:nil]; 48 | 49 | switch (self.index) { 50 | case 0: 51 | { 52 | self.navigationItem.title = @"富文本展示"; 53 | self.label.enableCopy = YES; 54 | //初始化配置 55 | CJLabelConfigure *configure = [CJLabel configureAttributes:nil isLink:NO activeLinkAttributes:nil parameter:nil clickLinkBlock:nil longPressBlock:nil]; 56 | //设置 'CJLabel' 字符不可点击 57 | configure.isLink = NO; 58 | attStr = [CJLabel configureAttrString:attStr withString:@"CJLabel" sameStringEnable:YES configure:configure]; 59 | //设置 `不同字体` 显示为粗体17的字号 60 | configure.attributes = @{NSFontAttributeName:[UIFont boldSystemFontOfSize:18]}; 61 | attStr = [CJLabel configureAttrString:attStr withString:@"不同字体" sameStringEnable:NO configure:configure]; 62 | //设置 `字体背景色` 填充背景色,以及填充区域圆角 63 | configure.attributes = @{kCJBackgroundFillColorAttributeName:[UIColor colorWithWhite:0.5 alpha:1],kCJBackgroundLineCornerRadiusAttributeName:@(0)}; 64 | attStr = [CJLabel configureAttrString:attStr withString:@"字体背景色" sameStringEnable:NO configure:configure]; 65 | //设置 `字体边框线` 边框线 66 | configure.attributes = @{kCJBackgroundStrokeColorAttributeName:[UIColor orangeColor]}; 67 | attStr = [CJLabel configureAttrString:attStr withString:@"字体边框线" sameStringEnable:NO configure:configure]; 68 | //指定文本添加删除线 69 | configure.attributes = @{kCJStrikethroughStyleAttributeName:@(1), 70 | kCJStrikethroughColorAttributeName:[UIColor redColor]}; 71 | attStr = [CJLabel configureAttrString:attStr withString:@"对指定文本添加删除线" sameStringEnable:NO configure:configure]; 72 | //指定位置插入图片 73 | NSRange imgRange = [attStr.string rangeOfString:@"插入图片"]; 74 | [configure removeAttributesForKey:kCJBackgroundStrokeColorAttributeName]; 75 | [configure removeAttributesForKey:kCJStrikethroughStyleAttributeName]; 76 | [configure removeAttributesForKey:kCJStrikethroughColorAttributeName]; 77 | 78 | attStr = [CJLabel insertViewAtAttrString:attStr view:@"CJLabel.png" viewSize:CGSizeMake(55, 45) atIndex:(imgRange.location+imgRange.length) lineAlignment:CJVerticalAlignmentBottom configure:configure]; 79 | //设置内边距 80 | self.label.textInsets = UIEdgeInsetsMake(10, 10, 10, 0); 81 | self.label.attributedText = attStr; 82 | self.attStr = attStr; 83 | } 84 | break; 85 | 86 | case 2: 87 | { 88 | self.navigationItem.title = @"点击链点"; 89 | UIBarButtonItem *item = [[UIBarButtonItem alloc]initWithTitle:@"删除链点" style:UIBarButtonItemStylePlain target:self action:@selector(itemClick:)]; 90 | item.tag = 100; 91 | self.navigationItem.rightBarButtonItem = item; 92 | 93 | attStr = [self configureLabelContent:attStr configure:configure]; 94 | self.label.attributedText = attStr; 95 | self.label.enableCopy = YES; 96 | self.attStr = attStr; 97 | } 98 | break; 99 | 100 | case 4: 101 | { 102 | self.navigationItem.title = @"图文混排"; 103 | 104 | UIBarButtonItem *item = [[UIBarButtonItem alloc]initWithTitle:@"···" style:UIBarButtonItemStylePlain target:self action:@selector(itemClick:)]; 105 | item.tag = 200; 106 | self.navigationItem.rightBarButtonItem = item; 107 | 108 | self.attStr = attStr; 109 | self.configure = configure; 110 | 111 | [self configureLabelContent:attStr verticalAlignment:CJVerticalAlignmentBottom configure:configure]; 112 | self.label.enableCopy = YES; 113 | break; 114 | } 115 | 116 | case 6: 117 | { 118 | self.navigationItem.title = @"指定文字不换行"; 119 | 120 | self.attStr = attStr; 121 | self.configure = configure; 122 | 123 | NSMutableAttributedString *resultStr = [[NSMutableAttributedString alloc]init]; 124 | 125 | NSRange lastRange = NSMakeRange(0, 0); 126 | NSArray *ary = [CJLabel sameLinkStringRangeArray:@"#CJLabel#" inAttString:attStr]; 127 | for (NSValue *value in ary) { 128 | NSRange range = [value rangeValue]; 129 | 130 | if (resultStr.length != range.location) { 131 | NSInteger length = range.location - (lastRange.location+lastRange.length); 132 | NSAttributedString *str = [attStr attributedSubstringFromRange:NSMakeRange(lastRange.location+lastRange.length, length)]; 133 | [resultStr appendAttributedString:str]; 134 | 135 | configure.attributes = @{NSForegroundColorAttributeName:[UIColor blueColor], 136 | NSFontAttributeName:[UIFont boldSystemFontOfSize:13]}; 137 | configure.activeLinkAttributes = @{NSForegroundColorAttributeName:[UIColor redColor], 138 | NSFontAttributeName:[UIFont boldSystemFontOfSize:13]}; 139 | configure.isLink = YES; 140 | NSAttributedString *label = [[NSAttributedString alloc]initWithString:@"#CJLabel#"]; 141 | NSMutableAttributedString *labelStr = [CJLabel initWithNonLineWrapAttributedString:label textInsets:UIEdgeInsetsZero configure:configure]; 142 | [resultStr appendAttributedString:labelStr]; 143 | lastRange = range; 144 | 145 | } 146 | } 147 | self.label.textInsets = UIEdgeInsetsMake(10, 10, 10, 10); 148 | self.label.enableCopy = YES; 149 | self.label.attributedText = resultStr; 150 | break; 151 | } 152 | 153 | default: 154 | break; 155 | } 156 | } 157 | 158 | - (NSMutableAttributedString *)configureLabelContent:(NSMutableAttributedString *)attStr configure:(CJLabelConfigure *)configure { 159 | 160 | __weak typeof(self)wSelf = self; 161 | //设置点击链点属性 162 | configure.attributes = @{ 163 | NSForegroundColorAttributeName:[UIColor blueColor], 164 | NSFontAttributeName:[UIFont boldSystemFontOfSize:15], 165 | kCJBackgroundStrokeColorAttributeName:[UIColor orangeColor], 166 | kCJBackgroundLineWidthAttributeName:@(1), 167 | kCJBackgroundFillColorAttributeName:[UIColor lightGrayColor] 168 | }; 169 | //设置点击高亮属性 170 | configure.activeLinkAttributes = @{ 171 | NSForegroundColorAttributeName:[UIColor redColor], 172 | kCJActiveBackgroundStrokeColorAttributeName:[UIColor redColor], 173 | kCJActiveBackgroundFillColorAttributeName:UIRGBColor(247,231,121,1) 174 | }; 175 | //链点自定义参数 176 | configure.parameter = @"参数为字符串"; 177 | //点击回调 178 | configure.clickLinkBlock = ^(CJLabelLinkModel *linkModel) { 179 | [wSelf clickLink:linkModel isImage:NO]; 180 | }; 181 | //长按回调 182 | configure.longPressBlock = ^(CJLabelLinkModel *linkModel) { 183 | [wSelf clicklongPressLink:linkModel isImage:NO]; 184 | }; 185 | //设置为可点击链点 186 | configure.isLink = YES; 187 | 188 | NSAttributedString *link = [CJLabel initWithNSString:@"CJLabel" strIdentifier:@"aa" configure:configure]; 189 | attStr = [CJLabel configureAttrString:attStr withAttributedString:link strIdentifier:@"aa" sameStringEnable:YES configure:configure]; 190 | 191 | return attStr; 192 | } 193 | 194 | - (void)configureLabelContent:(NSMutableAttributedString *)attStr verticalAlignment:(CJLabelVerticalAlignment)verticalAlignment configure:(CJLabelConfigure *)configure { 195 | 196 | __weak typeof(self)wSelf = self; 197 | attStr = [self configureLabelContent:attStr configure:configure]; 198 | 199 | //设置图片点击链点属性 200 | NSRange imageRange = [attStr.string rangeOfString:@"图片"]; 201 | CJLabelConfigure *imgConfigure = 202 | [CJLabel configureAttributes:@{kCJBackgroundStrokeColorAttributeName:[UIColor redColor], 203 | kCJBackgroundLineWidthAttributeName:@(1), 204 | kCJBackgroundLineCornerRadiusAttributeName:@(2)} 205 | isLink:YES 206 | activeLinkAttributes:@{kCJActiveBackgroundStrokeColorAttributeName:[UIColor lightGrayColor]} 207 | parameter:@"图片参数" 208 | clickLinkBlock:^(CJLabelLinkModel *linkModel){ 209 | [wSelf clickLink:linkModel isImage:YES]; 210 | } 211 | longPressBlock:^(CJLabelLinkModel *linkModel){ 212 | [wSelf clicklongPressLink:linkModel isImage:YES]; 213 | }]; 214 | 215 | UIImageView *imgView = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"CJLabel.png"]]; 216 | imgView.contentMode = UIViewContentModeScaleToFill; 217 | imgView.clipsToBounds = YES; 218 | attStr = [CJLabel insertViewAtAttrString:attStr view:imgView viewSize:CGSizeMake(100, 40) atIndex:(imageRange.location+imageRange.length) lineAlignment:verticalAlignment configure:imgConfigure]; 219 | 220 | // attStr = [CJLabel insertImageAtAttrString:attStr image:@"CJLabel.png" imageSize:CGSizeMake(45, 35) atIndex:(imageRange.location+imageRange.length) imagelineAlignment:verticalAlignment configure:imgConfigure]; 221 | 222 | self.label.attributedText = attStr; 223 | } 224 | 225 | - (void)itemClick:(UIBarButtonItem *)item { 226 | if (item.tag == 100) { 227 | //移除指定链点 228 | NSArray *linkRangeArray = [CJLabel sameLinkStringRangeArray:@"CJLabel" inAttString:self.attStr]; 229 | [self.label removeLinkAtRange:[linkRangeArray[0] rangeValue]]; 230 | item.enabled = NO; 231 | } 232 | else if (item.tag == 200) { 233 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"图片所在行垂直对齐方式" message:nil preferredStyle:UIAlertControllerStyleActionSheet]; 234 | [alert addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) { 235 | }]]; 236 | [alert addAction:[UIAlertAction actionWithTitle:@"顶部对齐" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { 237 | [self configureLabelContent:self.attStr verticalAlignment:CJVerticalAlignmentTop configure:self.configure]; 238 | }]]; 239 | [alert addAction:[UIAlertAction actionWithTitle:@"居中对齐" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { 240 | [self configureLabelContent:self.attStr verticalAlignment:CJVerticalAlignmentCenter configure:self.configure]; 241 | }]]; 242 | [alert addAction:[UIAlertAction actionWithTitle:@"底部对齐" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { 243 | [self configureLabelContent:self.attStr verticalAlignment:CJVerticalAlignmentBottom configure:self.configure]; 244 | }]]; 245 | [self presentViewController:alert animated:YES completion:nil]; 246 | } 247 | } 248 | 249 | - (void)clickLink:(CJLabelLinkModel *)linkModel isImage:(BOOL)isImage { 250 | NSString *title = [NSString stringWithFormat:@"点击链点 %@",linkModel.attributedString.string]; 251 | if (isImage) { 252 | title = [NSString stringWithFormat:@"点击链点图片:%@",linkModel.insertView]; 253 | } 254 | NSString *parameter = [NSString stringWithFormat:@"自定义参数:%@",linkModel.parameter]; 255 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:title message:parameter preferredStyle:UIAlertControllerStyleAlert]; 256 | UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil]; 257 | [alert addAction:cancel]; 258 | [self presentViewController:alert animated:YES completion:nil]; 259 | } 260 | 261 | - (void)clicklongPressLink:(CJLabelLinkModel *)linkModel isImage:(BOOL)isImage { 262 | NSString *title = [NSString stringWithFormat:@"长按点击: %@",linkModel.attributedString.string]; 263 | if (isImage) { 264 | title = [NSString stringWithFormat:@"长按点击图片:%@",linkModel.insertView]; 265 | } 266 | 267 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:title message:nil preferredStyle:UIAlertControllerStyleActionSheet]; 268 | UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil]; 269 | [alert addAction:cancel]; 270 | [self presentViewController:alert animated:YES completion:nil]; 271 | } 272 | 273 | @end 274 | -------------------------------------------------------------------------------- /Example/CJLabelTest/SecondDetailViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /Example/CJLabelTest/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // tableViewLabelDemo 4 | // 5 | // Created by YiChe on 16/6/13. 6 | // Copyright © 2016年 YiChe. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /Example/CJLabelTest/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // tableViewLabelDemo 4 | // 5 | // Created by YiChe on 16/6/13. 6 | // Copyright © 2016年 YiChe. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "Common.h" 11 | #import "AttributedTableViewCell.h" 12 | #import "FirstDetailViewController.h" 13 | #import "SecondDetailViewController.h" 14 | 15 | 16 | @interface ViewController () 17 | @property (nonatomic, strong) UITableView *tableView; 18 | @property (nonatomic, strong) NSMutableArray *espressos; 19 | 20 | @property (nonatomic, strong) AttributedTableViewCell *tempCell; 21 | 22 | @end 23 | 24 | @implementation ViewController 25 | 26 | - (AttributedTableViewCell *)tempCell { 27 | if (!_tempCell) { 28 | _tempCell = [[[NSBundle mainBundle] loadNibNamed:@"AttributedTableViewCell" owner:self options:Nil] lastObject]; 29 | } 30 | return _tempCell; 31 | } 32 | 33 | 34 | - (void)viewDidLoad { 35 | [super viewDidLoad]; 36 | 37 | self.edgesForExtendedLayout = UIRectEdgeNone; 38 | self.automaticallyAdjustsScrollViewInsets = NO; 39 | 40 | self.tableView = [[UITableView alloc]initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height) style:UITableViewStylePlain]; 41 | self.tableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; 42 | self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone; 43 | self.tableView.delegate = self; 44 | self.tableView.dataSource = self; 45 | self.tableView.tableFooterView = [[UIView alloc] init]; 46 | [self.view addSubview:self.tableView]; 47 | 48 | [self readFile]; 49 | } 50 | 51 | - (void)didReceiveMemoryWarning { 52 | [super didReceiveMemoryWarning]; 53 | // Dispose of any resources that can be recreated. 54 | } 55 | 56 | - (void)readFile { 57 | NSString *path = [[NSBundle mainBundle] pathForResource:@"Example" ofType:@"json"]; 58 | NSString *content = [[NSString alloc] initWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil]; 59 | 60 | NSData *JSONData = [content dataUsingEncoding:NSUTF8StringEncoding]; 61 | NSArray *responseJSON = nil; 62 | if (JSONData) { 63 | responseJSON = [NSJSONSerialization JSONObjectWithData:JSONData options:NSJSONReadingMutableContainers error:nil]; 64 | } 65 | 66 | /* 设置默认换行模式为:NSLineBreakByCharWrapping 67 | * 当Label的宽度不够显示内容或图片的时候就自动换行, 如果不自动换行, 超出一行的部分图片将不显示 68 | */ 69 | NSMutableParagraphStyle *paragraph = [[NSMutableParagraphStyle alloc] init]; 70 | paragraph.lineBreakMode = NSLineBreakByCharWrapping; 71 | paragraph.lineSpacing = 6; 72 | 73 | self.espressos = [NSMutableArray array]; 74 | for (int i = 0; i 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 | -------------------------------------------------------------------------------- /Example/CJLabelTestTests/CJLabelTestTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // CJLabelTestTests.m 3 | // CJLabelTestTests 4 | // 5 | // Created by C.K.Lian on 15/12/11. 6 | // Copyright © 2015年 C.K.Lian. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface CJLabelTestTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation CJLabelTestTests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | // Put setup code here. This method is called before the invocation of each test method in the class. 20 | } 21 | 22 | - (void)tearDown { 23 | // Put teardown code here. This method is called after the invocation of each test method in the class. 24 | [super tearDown]; 25 | } 26 | 27 | - (void)testExample { 28 | // This is an example of a functional test case. 29 | // Use XCTAssert and related functions to verify your tests produce the correct results. 30 | } 31 | 32 | - (void)testPerformanceExample { 33 | // This is an example of a performance test case. 34 | [self measureBlock:^{ 35 | // Put the code you want to measure the time of here. 36 | }]; 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /Example/CJLabelTestTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Example/CJLabelTestUITests/CJLabelTestUITests.m: -------------------------------------------------------------------------------- 1 | // 2 | // CJLabelTestUITests.m 3 | // CJLabelTestUITests 4 | // 5 | // Created by C.K.Lian on 15/12/11. 6 | // Copyright © 2015年 C.K.Lian. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface CJLabelTestUITests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation CJLabelTestUITests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | 22 | // In UI tests it is usually best to stop immediately when a failure occurs. 23 | self.continueAfterFailure = NO; 24 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 25 | [[[XCUIApplication alloc] init] launch]; 26 | 27 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 28 | } 29 | 30 | - (void)tearDown { 31 | // Put teardown code here. This method is called after the invocation of each test method in the class. 32 | [super tearDown]; 33 | } 34 | 35 | - (void)testExample { 36 | // Use recording to get started writing UI tests. 37 | // Use XCTAssert and related functions to verify your tests produce the correct results. 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /Example/CJLabelTestUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 ChiJinLian 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CJLabel 2 | 3 | `CJLabel`继承自`UILabel`,在支持`UILabel`所有属性的基础上,还提供富文本、图文混排、任意view插入展示、自定义点击链点设置、长按(双击)唤起`UIMenuController`选择复制文本等功能。 4 | 5 | ## 特性简介 6 | 1. 禁止使用`-init`初始化!! 7 | 2. `enableCopy` 长按或双击可唤起`UIMenuController`进行选择、全选、复制文本操作 8 | 3. `attributedText` 与 `text` 均可设置富文本 9 | 4. 不支持`NSAttachmentAttributeName`,`NSTextAttachment`!!
显示图片请调用:
10 | `+ initWithView:viewSize:lineAlignment:configure:`或者
11 | `+ insertViewAtAttrString:view:viewSize:atIndex:lineAlignment:configure:`方法初始化`NSAttributedString`后显示 12 | 5. `extendsLinkTouchArea`设置是否扩大链点点击识别范围 13 | 6. `shadowRadius`设置文本阴影模糊半径 14 | 7. `textInsets` 设置文本内边距 15 | 8. `verticalAlignment` 设置垂直方向的文本对齐方式。注意与显示图片时候的`imagelineAlignment`作区分,`self.verticalAlignment`对应的是整体文本在垂直方向的对齐方式,而`imagelineAlignment`只对图片所在行的垂直对齐方式有效 16 | 9. `delegate` 点击链点代理 17 | 10. `attributedTruncationToken`自定义截断字符,默认"...",只针对`self.lineBreakMode`的以下三种值有效,假如`attributedTruncationToken`=`***`,则:
18 | `NSLineBreakByTruncatingHead, // 头部截断: "***wxyz"`
19 | `NSLineBreakByTruncatingTail, // 中间截断: "abcd***"`
20 | `NSLineBreakByTruncatingMiddle // 尾部截断: "ab***yz"` 21 | 11. `kCJBackgroundFillColorAttributeName` 背景填充颜色,属性优先级低于`NSBackgroundColorAttributeName`如果设置`NSBackgroundColorAttributeName`会忽略`kCJBackgroundFillColorAttributeName`的设置 22 | 12. `kCJBackgroundStrokeColorAttributeName ` 背景边框线颜色 23 | 13. `kCJBackgroundLineWidthAttributeName ` 背景边框线宽度 24 | 14. `kCJBackgroundLineCornerRadiusAttributeName ` 背景边框线圆角弧度 25 | 15. `kCJActiveBackgroundFillColorAttributeName ` 点击时候的背景填充颜色属性优先级同 26 | `kCJBackgroundFillColorAttributeName` 27 | 16. `kCJActiveBackgroundStrokeColorAttributeName ` 点击时候的背景边框线颜色 28 | 17. 支持添加自定义样式、可点击(长按)的文本点击链点 29 | 18. 支持 Interface Builder 30 | 31 | 32 | ##### CJLabel 已知 Bug 33 | 34 | `numberOfLines`大于0且小于实际`label.numberOfLines`,同时`verticalAlignment`不等于`CJContentVerticalAlignmentTop`时,文本显示位置有偏差。如下图所示:
35 |
36 | 37 |
38 | 39 | ## CJLabel引用 40 | ##### 1. 直接导入 41 | 下载demo,将CJLabel文件夹导入项目,引用头文件 `#import "CJLabel.h"` 42 | ##### 2. CocoaPods安装 43 | ```ruby 44 | pod 'CJLabel' 45 | 46 | ``` 47 | 48 | ## 用法 49 | * 根据NSAttributedString计算CJLabel的size大小 50 | 51 | ```objective-c 52 | CGSize size = [CJLabel sizeWithAttributedString:str withConstraints:CGSizeMake(320, CGFLOAT_MAX) limitedToNumberOfLines:0]; 53 | ``` 54 | * 指定内边距以及限定行数计算CJLabel的size大小 55 | ```objective-c 56 | CGSize size = [CJLabel sizeWithAttributedString:str withConstraints:CGSizeMake(320, CGFLOAT_MAX) limitedToNumberOfLines:0 textInsets:3]; 57 | ``` 58 | 59 | * 设置富文本展示 60 |
61 | 62 |
63 | 64 | ```objective-c 65 | //初始化配置 66 | CJLabelConfigure *configure = [CJLabel configureAttributes:nil isLink:NO activeLinkAttributes:nil parameter:nil clickLinkBlock:nil longPressBlock:nil]; 67 | //设置配置属性 68 | configure.attributes = @{NSFontAttributeName:[UIFont boldSystemFontOfSize:18]}; 69 | //设置指定字符属性 70 | attStr = [CJLabel configureAttrString:attStr withString:@"不同字体" sameStringEnable:NO configure:configure]; 71 | NSRange imgRange = [attStr.string rangeOfString:@"插入图片"]; 72 | //移除指定属性 73 | [configure removeAttributesForKey:kCJBackgroundStrokeColorAttributeName]; 74 | //指定位置插入图片 75 | UIImageView *imgView = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"CJLabel.png"]]; 76 | imgView.contentMode = UIViewContentModeScaleAspectFill; 77 | imgView.clipsToBounds = YES; 78 | attStr = [CJLabel insertViewAtAttrString:attStr view:imgView viewSize:CGSizeMake(55, 45) atIndex:(imgRange.location+imgRange.length) lineAlignment:CJVerticalAlignmentBottom configure:configure]; 79 | //设置内边距 80 | self.label.textInsets = UIEdgeInsetsMake(10, 10, 10, 0); 81 | self.label.attributedText = attStr; 82 | ``` 83 | * 垂直对齐、选择复制 84 |
85 | 86 |
87 | 88 | ```objective-c 89 | //设置垂直对齐方式 90 | self.label.verticalAlignment = CJVerticalAlignmentCenter; 91 | self.label.text = self.attStr; 92 | //支持选择复制 93 | self.label.enableCopy = YES; 94 | ``` 95 | * 设置文字、图片点击链点 96 |
97 | 98 |
99 | 100 | ```objective-c 101 | //设置点击链点属性 102 | configure.attributes = @{NSForegroundColorAttributeName:[UIColor blueColor]}; 103 | //设置点击高亮属性 104 | configure.activeLinkAttributes = @{NSForegroundColorAttributeName:[UIColor redColor]}; 105 | //链点自定义参数 106 | configure.parameter = @"参数为字符串"; 107 | //点击回调(也可通过设置self.label.delegate = self代理,返回点击回调事件) 108 | configure.clickLinkBlock = ^(CJLabelLinkModel *linkModel) { 109 | //do something 110 | }; 111 | //长按回调 112 | configure.longPressBlock = ^(CJLabelLinkModel *linkModel) { 113 | //do something 114 | }; 115 | //设置为可点击链点 116 | configure.isLink = YES; 117 | //设置点击链点 118 | attStr = [CJLabel configureAttrString:attStr 119 | withString:@"CJLabel" 120 | sameStringEnable:YES 121 | configure:configure]; 122 | //设置图片点击链点属性 123 | NSRange imageRange = [attStr.string rangeOfString:@"图片"]; 124 | CJLabelConfigure *imgConfigure = 125 | [CJLabel configureAttributes:@{kCJBackgroundStrokeColorAttributeName:[UIColor redColor]} 126 | isLink:YES 127 | activeLinkAttributes:@{kCJActiveBackgroundStrokeColorAttributeName:[UIColor lightGrayColor]} 128 | parameter:@"图片参数" 129 | clickLinkBlock:^(CJLabelLinkModel *linkModel){ 130 | [self clickLink:linkModel isImage:YES]; 131 | } 132 | longPressBlock:^(CJLabelLinkModel *linkModel){ 133 | [self clicklongPressLink:linkModel isImage:YES]; 134 | }]; 135 | attStr = [CJLabel insertViewAtAttrString:attStr view:@"CJLabel.png" viewSize:CGSizeMake(45, 35) atIndex:(imageRange.location+imageRange.length) lineAlignment:verticalAlignment configure:imgConfigure]; 136 | self.label.attributedText = attStr; 137 | //支持选择复制 138 | self.label.enableCopy = YES; 139 | ``` 140 | 141 | * 自定义截断文本,并设置为可点击 142 |
143 | 144 |
145 | 146 | ```objective-c 147 | //配置链点属性 148 | configure.isLink = YES; 149 | configure.clickLinkBlock = ^(CJLabelLinkModel *linkModel) { 150 | //点击 `……全文` 151 | [self clickTruncationToken:linkModel]; 152 | }; 153 | configure.attributes = @{NSForegroundColorAttributeName:[UIColor blueColor],NSFontAttributeName:[UIFont systemFontOfSize:13]}; 154 | //自定义截断字符为:"……全文" 155 | NSAttributedString *truncationToken = [CJLabel initWithAttributedString:[[NSAttributedString alloc]initWithString:@"……全文"] strIdentifier:@"TruncationToken" configure:configure]; 156 | //设置行尾截断 157 | self.label.lineBreakMode = NSLineBreakByTruncatingTail; 158 | self.label.attributedTruncationToken = truncationToken; 159 | //设置点击链点 160 | attStr = [CJLabel configureAttrString:attStr withAttributedString:truncationToken strIdentifier:@"TruncationToken" sameStringEnable:NO configure:configure]; 161 | self.label.attributedText = attStr; 162 | //支持选择复制 163 | self.label.enableCopy = YES; 164 | ``` 165 | 166 | ## 版本说明 167 | * ***V4.7.0***
168 | 新增不可换行标签功能,优化图文混排展示 169 | 170 | * ***V4.6.0***
171 | 支持显示任意view 172 | 173 | * ***V4.5.0 V4.5.1 V4.5.3***
174 | 增加`attributedTruncationToken`属性,支持自定义截断字符;增加`kCJStrikethroughStyleAttributeName、kCJStrikethroughColorAttributeName`属性,可对指定文本添加删除线 175 | 176 | * ***V4.4.0***
177 | 优化NSAttributedString链点属性设置 178 | 179 | * ***V4.0.0***
180 | 新增`enableCopy`属性,支持选择、全选、复制功能,类似`UITextView`的选择复制效果。 181 | 182 | * ***V3.0.0***
183 | 优化富文本配置方法,新增CJLabelConfigure类,简化方法调用,增加对NSAttributedString点击链点的判断(比如对于两个重名用户:@lele 和 @lele,可以分别设置不同的点击响应事件)
184 | ***注意*** 185 | ***`V3.0.0`*** 版本引入`CJLabelConfigure`类,优化了NSAttributedString的设置,旧的配置API不再支持。相关调用请参照以下相关方法
186 | `+ initWithImage:imageSize:imagelineAlignment:configure:`
187 | `+ initWithString:configure:`
188 | `+ initWithAttributedString:strIdentifier:configure:`
189 | 190 | * ***V2.1.2***
191 | 可修改图片所在行在垂直方向的对齐方式(只针对当前行),有居上、居中、居下选项,默认居下 192 | 193 | * ***V2.1.1***
194 | 修复单行文字时候点击链点的判断,增加delegate 195 | 196 | * ***V2.0.0***
197 | 优化点击链点响应判断,增加插入图片、插入图片链点、点击链点背景色填充、点击链点边框线描边等功能 198 | v2.0.0之后版本与v1.x.x版本差别较大,基本上重写了增加以及移除点击链点的API 199 | 200 | * ***V1.0.2***
201 | 点击链点增加扩展属性parameter 202 | 203 | * ***V1.0.1***
204 | 增加文本中内容相同的链点能够响应点击属性sameLinkEnable,必须在设置self.attributedText前赋值,默认值为NO,只取文本中首次出现的链点。 205 | 206 | * ***V1.0.0***
207 | 支持链点点击响应 208 | 209 | 210 | ## 许可证 211 | CJLabel 使用 MIT 许可证,详情见 LICENSE 文件。 212 | 213 | ## 更多 214 | [深入理解 iOS 图文混排原理并自定义图文控件](https://www.infoq.cn/article/cy916KUJYK7GA3p2VjZH) 215 | [CJLabel富文本三 —— UILabel支持选择复制以及实现原理](https://www.jianshu.com/p/7de3e6d19e31) --------------------------------------------------------------------------------