├── .DS_Store ├── .gitignore ├── Example ├── Podfile ├── RZColorful.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── RZColorful-Example.xcscheme ├── RZColorful.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── xcuserdata │ │ └── rztime.xcuserdatad │ │ ├── UserInterfaceState.xcuserstate │ │ └── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist ├── RZColorful │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── Demo │ │ ├── AttributedStringUseDemoVC.h │ │ ├── AttributedStringUseDemoVC.m │ │ ├── MDToHtmlViewController.h │ │ ├── MDToHtmlViewController.m │ │ ├── RZFoldLabelVC.h │ │ ├── RZFoldLabelVC.m │ │ ├── RZTestViewController.h │ │ ├── RZTestViewController.m │ │ ├── RZTextViewVC.h │ │ └── RZTextViewVC.m │ ├── Images.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ ├── Contents.json │ │ └── image.imageset │ │ │ ├── Contents.json │ │ │ └── image@3x.png │ ├── RZAppDelegate.h │ ├── RZAppDelegate.m │ ├── RZColorful-Info.plist │ ├── RZColorful-Prefix.pch │ ├── RZViewController.h │ ├── RZViewController.m │ ├── en.lproj │ │ └── InfoPlist.strings │ └── main.m └── Tests │ ├── Tests-Info.plist │ ├── Tests-Prefix.pch │ ├── Tests.m │ └── en.lproj │ └── InfoPlist.strings ├── LICENSE ├── README.md ├── RZColorful.podspec ├── RZColorful ├── Assets │ └── .gitkeep └── Classes │ ├── AttributeCore │ ├── NSAttributedString+RZColorful.h │ ├── NSAttributedString+RZColorful.m │ ├── NSAttributedString+RZHtml.h │ ├── NSAttributedString+RZHtml.m │ ├── NSString+RZCode.h │ ├── NSString+RZCode.m │ ├── RZColorfulAttribute.h │ ├── RZColorfulAttribute.m │ ├── RZColorfulConferrer.h │ ├── RZColorfulConferrer.m │ ├── RZImageAttachment.h │ ├── RZImageAttachment.m │ ├── RZParagraphStyle.h │ ├── RZParagraphStyle.m │ ├── RZShadow.h │ └── RZShadow.m │ ├── RZColorful.h │ └── UICore │ ├── LabelFoldHelper.h │ ├── LabelFoldHelper.m │ ├── MarkdownRZ.h │ ├── MarkdownRZ.m │ ├── RZTapActionHelper.h │ ├── RZTapActionHelper.m │ ├── UIButton+RZColorful.h │ ├── UIButton+RZColorful.m │ ├── UILabel+RZColorful.h │ ├── UILabel+RZColorful.m │ ├── UITextField+RZColorful.h │ ├── UITextField+RZColorful.m │ ├── UITextField+SelectedRange.h │ ├── UITextField+SelectedRange.m │ ├── UITextView+RZColorful.h │ ├── UITextView+RZColorful.m │ ├── UIView+RZContinueFirstResponder.h │ └── UIView+RZContinueFirstResponder.m ├── UpdateLog.md ├── _Pods.xcodeproj ├── image_1.jpg ├── image_2.png └── line.png /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rztime/RZColorful/ab5ece13b6258431cccd1bb44f8d3350ac2af53e/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Pods/ 2 | Podfile.lock 3 | */xcuserdata/ 4 | -------------------------------------------------------------------------------- /Example/Podfile: -------------------------------------------------------------------------------- 1 | use_frameworks! 2 | 3 | platform :ios, '11.0' 4 | 5 | target 'RZColorful_Example' do 6 | pod 'RZColorful', :path => '../' 7 | pod 'Masonry' 8 | target 'RZColorful_Tests' do 9 | inherit! :search_paths 10 | 11 | # pod 'FBSnapshotTestCase' 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /Example/RZColorful.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/RZColorful.xcodeproj/xcshareddata/xcschemes/RZColorful-Example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 85 | 91 | 92 | 93 | 94 | 96 | 97 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /Example/RZColorful.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Example/RZColorful.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/RZColorful.xcworkspace/xcuserdata/rztime.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rztime/RZColorful/ab5ece13b6258431cccd1bb44f8d3350ac2af53e/Example/RZColorful.xcworkspace/xcuserdata/rztime.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /Example/RZColorful.xcworkspace/xcuserdata/rztime.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /Example/RZColorful/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 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /Example/RZColorful/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 | -------------------------------------------------------------------------------- /Example/RZColorful/Demo/AttributedStringUseDemoVC.h: -------------------------------------------------------------------------------- 1 | // 2 | // AttributedStringUseDemoVC.h 3 | // RZColorful_Example 4 | // 5 | // Created by rztime on 2022/1/25. 6 | // Copyright © 2022 rztime. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface AttributedStringUseDemoVC : UIViewController 14 | 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /Example/RZColorful/Demo/AttributedStringUseDemoVC.m: -------------------------------------------------------------------------------- 1 | // 2 | // AttributedStringUseDemoVC.m 3 | // RZColorful_Example 4 | // 5 | // Created by rztime on 2022/1/25. 6 | // Copyright © 2022 rztime. All rights reserved. 7 | // 8 | 9 | #import "AttributedStringUseDemoVC.h" 10 | #import 11 | 12 | @interface AttributedStringUseDemoVC () 13 | @property (nonatomic, strong) UILabel *label; 14 | @end 15 | 16 | @implementation AttributedStringUseDemoVC 17 | 18 | - (void)viewDidLoad { 19 | [super viewDidLoad]; 20 | self.view.backgroundColor = UIColor.whiteColor; 21 | _label = [[UILabel alloc] init]; 22 | _label.numberOfLines = 0; 23 | [self.view addSubview:_label]; 24 | _label.frame = self.view.bounds; 25 | NSAttributedString *place = [NSAttributedString rz_colorfulConfer:^(RZColorfulConferrer * _Nullable confer) { 26 | confer.text(@"...").font([UIFont systemFontOfSize:18]).textColor([UIColor redColor]); 27 | }]; 28 | NSAttributedString *attr = [NSAttributedString rz_colorfulConfer:^(RZColorfulConferrer * _Nullable confer) { 29 | confer.htmlText(@"\n\n\n标签富文本
百度一下").font([UIFont systemFontOfSize:16]); 30 | confer.text(@"\n正文使用方法:常规 字体 + 颜色\n\n").font([UIFont systemFontOfSize:16]).textColor(UIColor.blackColor); 31 | 32 | confer.text(@"添加本地图片 ").font([UIFont systemFontOfSize:20]).textColor(UIColor.redColor); 33 | confer.image([UIImage imageNamed:@"image"]).size(CGSizeMake(40, 40), RZHorizontalAlignCenter, [UIFont systemFontOfSize:20]).tapActionByLable(@"2"); // 点击事件 34 | confer.text(@" "); 35 | confer.image([UIImage imageNamed:@"image"]).size(CGSizeMake(40, 40), RZHorizontalAlignCenter, [UIFont systemFontOfSize:20]).custom(NSTapActionByLabelAttributeName, @"3"); // 自定义点击事件 36 | confer.text(@" 图片可对齐文本\n\n").font([UIFont systemFontOfSize:20]).textColor(UIColor.redColor); 37 | 38 | confer.text(@"正文使用方法:斜体").font([UIFont systemFontOfSize:16]).textColor(UIColor.blackColor).italic(@1); 39 | confer.text(@"\n正文使用方法:删除线").font([UIFont systemFontOfSize:16]).textColor(UIColor.blackColor).strikeThrough(NSUnderlineStyleSingle).strikeThroughColor(UIColor.redColor); 40 | confer.text(@"\n正文使用方法:下划线").font([UIFont systemFontOfSize:16]).textColor(UIColor.blackColor).underLineStyle(NSUnderlineStyleSingle).underLineColor(UIColor.redColor); 41 | confer.text(@"\n正文使用方法:描边\n").font([UIFont systemFontOfSize:16]).textColor(UIColor.blackColor).strokeWidth(@1).strokeColor(UIColor.redColor); 42 | confer.text(@"正文使用方法:背景色").font([UIFont systemFontOfSize:16]).textColor(UIColor.blackColor).backgroundColor(UIColor.lightGrayColor); 43 | confer.text(@"\n文本的点击事件").font([UIFont systemFontOfSize:16]); 44 | confer.text(@"可点击文本").textColor(UIColor.redColor).font([UIFont systemFontOfSize:16]).tapActionByLable(@"点击的id"); 45 | confer.text(@"\n"); 46 | confer.text(@"段落样式的方法设置段落样式的方法设置段落样式的方法设置段落样式的方法设置段落样式的方法设置段落样式的方法设置段落样式的方法设置段落样式的方法设置段落样式的方法设置段落样式的方法设置段落样式的方法设置段落样式的方法设置段落样式的方法设置段落样式的方法设置段落样式的方法设置段落样式的方法设置段落样式的方法设置段落样式的方法设置段落样式的方法设置段落样式的方法设置段落样式的方法设置段落样式的方法设置").font([UIFont systemFontOfSize:16]).textColor(UIColor.blackColor).paragraphStyle.lineSpacing(20).paragraphSpacingBefore(10).lineBreakMode(NSLineBreakByTruncatingTail).numberOfLines(4, self.view.bounds.size.width, place); 47 | }]; 48 | _label.attributedText = [attr rz_markText:@"段落" attribute:@{NSForegroundColorAttributeName: UIColor.redColor}]; 49 | [_label rz_tapAction:^(UILabel * _Nonnull label, NSString * _Nonnull tapActionId, NSRange range) { 50 | NSLog(@"%@", tapActionId); // print: 点击的id 51 | }]; 52 | } 53 | 54 | /* 55 | #pragma mark - Navigation 56 | 57 | // In a storyboard-based application, you will often want to do a little preparation before navigation 58 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 59 | // Get the new view controller using [segue destinationViewController]. 60 | // Pass the selected object to the new view controller. 61 | } 62 | */ 63 | 64 | @end 65 | -------------------------------------------------------------------------------- /Example/RZColorful/Demo/MDToHtmlViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // MDToHtmlViewController.h 3 | // RZColorful_Example 4 | // 5 | // Created by rztime on 2025/3/14. 6 | // Copyright © 2025 rztime. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface MDToHtmlViewController : UIViewController 14 | 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /Example/RZColorful/Demo/MDToHtmlViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // MDToHtmlViewController.m 3 | // RZColorful_Example 4 | // 5 | // Created by rztime on 2025/3/14. 6 | // Copyright © 2025 rztime. All rights reserved. 7 | // 8 | 9 | #import "MDToHtmlViewController.h" 10 | #import 11 | #import "RZColorful.h" 12 | @interface MDToHtmlViewController () 13 | @property (nonatomic, strong) WKWebView *webView; 14 | @end 15 | 16 | @implementation MDToHtmlViewController 17 | 18 | - (void)viewDidLoad { 19 | [super viewDidLoad]; 20 | _webView = [[WKWebView alloc] initWithFrame:self.view.bounds]; 21 | [self.view addSubview:_webView]; 22 | NSString * text = @"## 关于RZColorful\n\ 23 | * 对NSAttributeString的初始化做支持\n\ 24 | * 支持UILabel、UITextView、UITextField的attributedText的设置。\n\ 25 | * 包含的属性快捷设置:\n\ 26 | * 段落样式\n\ 27 | * 阴影\n\ 28 | * 文本字体、颜色\n\ 29 | * 文本所在区域对应的背景颜色\n\ 30 | * 连体字\n\ 31 | * 字间距\n\ 32 | * 删除线、下划线,及其线条颜色\n\ 33 | * 描边,及其颜色\n\ 34 | * 斜体字\n\ 35 | * 拉伸\n\ 36 | * 通过html源码加载富文本\n\ 37 | * 通过url添加图片到富文本\n\ 38 | * 等等\n\ 39 | \n\ 40 | 示例代码:\n\ 41 | \n\ 42 | \n\ 43 | ```swift\n\ 44 | text.rz.colorfulConfer { (confer) in \n\ 45 | confer.paragraphStyle?.lineSpacing(10).paragraphSpacingBefore(15)\n\ 46 | confer.image(UIImage.init(named: \"indexMore\"))?.bounds(CGRect.init(x: 0, y: 0, width: 20, height: 20))\n\ 47 | confer.text(\" 姓名 : \")?.font(UIFont.systemFont(ofSize: 15)).textColor(.gray)\n\ 48 | confer.text(\"rztime\")?.font(UIFont.systemFont(ofSize: 15)).textColor(.black)\n\ 49 | }\n\ 50 | ```\n\ 51 | "; 52 | NSString *md = text; 53 | NSString *html = [MarkdownRZ parse:md]; 54 | /// 解析md后的html,没有任何的样式,需要自行组装head里style:颜色、字号、列表、代码等等) 55 | [_webView loadHTMLString:[self mdcss:html] baseURL:nil]; 56 | } 57 | 58 | - (NSString *)mdcss:(NSString *)html { 59 | return [[NSString alloc] initWithFormat: @"\ 60 | \ 61 | \ 62 | \ 63 | \ 64 | \ 65 | \ 89 | \ 90 | \ 91 | %@\ 92 | \ 93 | \ 94 | ", html]; 95 | } 96 | @end 97 | -------------------------------------------------------------------------------- /Example/RZColorful/Demo/RZFoldLabelVC.h: -------------------------------------------------------------------------------- 1 | // 2 | // RZFoldLabelVC.h 3 | // RZColorful_Example 4 | // 5 | // Created by rztime on 2022/1/26. 6 | // Copyright © 2022 rztime. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface FoldModel : NSObject 14 | @property (nonatomic, strong) NSAttributedString *text; 15 | @property (nonatomic, strong) NSAttributedString *showAll; 16 | @property (nonatomic, strong) NSAttributedString *showFold; 17 | @property (nonatomic, assign) BOOL isFold; 18 | @end 19 | 20 | @interface RZFoldLabelVC : UIViewController 21 | 22 | @end 23 | 24 | @interface RZFoldTestCell : UITableViewCell 25 | 26 | @property (nonatomic, strong) UILabel *titleLabel; 27 | 28 | @end 29 | 30 | NS_ASSUME_NONNULL_END 31 | -------------------------------------------------------------------------------- /Example/RZColorful/Demo/RZFoldLabelVC.m: -------------------------------------------------------------------------------- 1 | // 2 | // RZFoldLabelVC.m 3 | // RZColorful_Example 4 | // 5 | // Created by rztime on 2022/1/26. 6 | // Copyright © 2022 rztime. All rights reserved. 7 | // 8 | 9 | #import "RZFoldLabelVC.h" 10 | #import 11 | #import 12 | 13 | @implementation FoldModel 14 | 15 | - (instancetype)init { 16 | if (self = [super init]) { 17 | _text = [NSAttributedString rz_colorfulConfer:^(RZColorfulConferrer * _Nullable confer) { 18 | confer.text(@"百年来,我们党团结带领人民进行的一切奋斗、一切牺牲、一切创造,都是在践行为中国人民谋幸福、为中华民族谋复兴的初心使命\n中国共产党的成就和贡献,不仅是历史性的,也是世界性的\n中国共产党人的历史自信,既是对奋斗成就的自信,也是对奋斗精神的自信\n从一大会址复原场景到遵义会议复原景观,从开国大典影像到创办经济特区图片,从脱贫攻坚数据图表到火神山、雷神山医院模型……走进中国共产党历史展览馆,一幅幅图片、一件件文物、一个个场景,全方位、全过程、全景式、史诗般展现中国共产党波澜壮阔的百年历程,让参观者从内心深处感受震撼,升腾起昂扬向上的力量。").font([UIFont systemFontOfSize:16]).textColor(UIColor.blackColor); 19 | }]; 20 | _showAll = [NSAttributedString rz_colorfulConfer:^(RZColorfulConferrer * _Nullable confer) { 21 | confer.text(@"...").font([UIFont systemFontOfSize:16]).textColor(UIColor.blackColor); 22 | confer.text(@"显示全文").font([UIFont systemFontOfSize:16]).textColor(UIColor.redColor).tapActionByLable(@"showAll"); 23 | }]; 24 | _showFold = [NSAttributedString rz_colorfulConfer:^(RZColorfulConferrer * _Nullable confer) { 25 | confer.text(@"...").font([UIFont systemFontOfSize:16]).textColor(UIColor.blackColor); 26 | confer.text(@"折叠").font([UIFont systemFontOfSize:16]).textColor(UIColor.redColor).tapActionByLable(@"showFold"); 27 | }]; 28 | _isFold = YES; 29 | } 30 | return self; 31 | } 32 | 33 | @end 34 | 35 | 36 | @interface RZFoldLabelVC () 37 | @property (nonatomic, strong) UITableView *tableView; 38 | 39 | @property (nonatomic, strong) NSMutableArray *items; 40 | @end 41 | 42 | @implementation RZFoldLabelVC 43 | 44 | - (void)viewDidLoad { 45 | [super viewDidLoad]; 46 | 47 | _tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain]; 48 | _tableView.rowHeight = UITableViewAutomaticDimension; 49 | _tableView.delegate = self; 50 | _tableView.dataSource = self; 51 | // _tableView.estimatedRowHeight = 100; 52 | [self.view addSubview:_tableView]; 53 | _items = @[].mutableCopy; 54 | for (NSInteger i = 0; i < 50; i++) { 55 | FoldModel *model = [[FoldModel alloc] init]; 56 | [_items addObject:model]; 57 | } 58 | } 59 | 60 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 61 | return _items.count; 62 | } 63 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 64 | RZFoldTestCell *cell = (RZFoldTestCell *)[tableView dequeueReusableCellWithIdentifier:@"cell"]; 65 | if (cell == nil) { 66 | cell = [[RZFoldTestCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"]; 67 | __weak typeof(self) weakSelf = self; 68 | [cell.titleLabel rz_tapAction:^(UILabel * _Nonnull label, NSString * _Nonnull tapActionId, NSRange range) { 69 | if ([tapActionId isEqualToString:@"showAll"]) { 70 | NSLog(@"显示全文"); 71 | } else if ([tapActionId isEqualToString:@"showFold"]) { 72 | NSLog(@"折叠"); 73 | } 74 | FoldModel *model = weakSelf.items[label.tag]; 75 | model.isFold = !model.isFold; 76 | [weakSelf.tableView reloadData]; 77 | }]; 78 | } 79 | cell.titleLabel.tag = indexPath.row; 80 | FoldModel *model = _items[indexPath.row]; 81 | [cell.titleLabel rz_setAttributedString:model.text maxLine:4 maxWidth:self.view.bounds.size.width - 40 isFold:model.isFold showAllText:model.showAll showFoldText:model.showFold]; 82 | return cell; 83 | } 84 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 85 | [tableView deselectRowAtIndexPath:indexPath animated:false]; 86 | } 87 | 88 | @end 89 | 90 | @implementation RZFoldTestCell 91 | 92 | - (UILabel *)titleLabel { 93 | if (!_titleLabel) { 94 | self.selectionStyle = UITableViewCellSelectionStyleNone; 95 | _titleLabel = [[UILabel alloc] init]; 96 | [self.contentView addSubview:_titleLabel]; 97 | [_titleLabel mas_makeConstraints:^(MASConstraintMaker *make) { 98 | make.edges.equalTo(self.contentView).insets(UIEdgeInsetsMake(10, 20, 10, 20)); 99 | }]; 100 | } 101 | return _titleLabel; 102 | } 103 | 104 | @end 105 | -------------------------------------------------------------------------------- /Example/RZColorful/Demo/RZTestViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // RZTestViewController.h 3 | // RZColorful_Example 4 | // 5 | // Created by rztime on 2025/2/12. 6 | // Copyright © 2025 rztime. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface RZTestViewController : UIViewController 14 | 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /Example/RZColorful/Demo/RZTestViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // RZTestViewController.m 3 | // RZColorful_Example 4 | // 5 | // Created by rztime on 2025/2/12. 6 | // Copyright © 2025 rztime. All rights reserved. 7 | // 8 | 9 | #import "RZTestViewController.h" 10 | #import "Masonry.h" 11 | #import "RZColorful.h" 12 | @interface RZTestViewController () 13 | @property(nonatomic, strong) UILabel * label; 14 | @end 15 | 16 | @implementation RZTestViewController 17 | 18 | - (void)viewDidLoad { 19 | [super viewDidLoad]; 20 | self.view.backgroundColor = [UIColor whiteColor]; 21 | self.label = [[UILabel alloc] init]; 22 | self.label.numberOfLines = 0; 23 | [self.view addSubview:self.label]; 24 | [self.label mas_makeConstraints:^(MASConstraintMaker *make) { 25 | make.left.right.equalTo(self.view).inset(20); 26 | make.top.equalTo(self.view).inset(100); 27 | }]; 28 | NSString * text = @"壹贰叁\n肆伍陆柒捌玖拾壹拾壹壹拾贰壹拾叁壹拾肆壹拾伍壹拾陆壹拾柒壹拾捌壹拾玖1\n贰拾贰拾壹贰拾贰贰拾叁贰拾肆贰拾伍贰拾陆贰拾柒贰拾捌贰拾玖\n叁拾叁拾壹叁拾贰叁拾叁叁拾肆叁拾伍叁拾陆叁拾柒叁拾捌叁拾玖\n肆拾肆拾壹肆拾贰肆拾叁肆拾肆肆拾伍肆拾陆肆拾柒肆拾捌肆拾玖伍拾。"; 29 | NSAttributedString * place = [NSAttributedString rz_colorfulConfer:^(RZColorfulConferrer * _Nullable confer) { 30 | confer.text(@"...").font([UIFont systemFontOfSize:30]).textColor([UIColor redColor]).tapActionByLable(@"1"); 31 | }]; 32 | NSAttributedString * test = [NSAttributedString rz_colorfulConfer:^(RZColorfulConferrer * _Nullable confer) { 33 | confer.text(text).font([UIFont systemFontOfSize:30]).textColor([UIColor blackColor]).tapActionByLable(@"1"); 34 | }]; 35 | _label.attributedText = [test rz_attributedStringBy:2 maxWidth:self.view.frame.size.width - 40 lineBreakMode:NSLineBreakByClipping placeHolder:place]; 36 | } 37 | 38 | /* 39 | #pragma mark - Navigation 40 | 41 | // In a storyboard-based application, you will often want to do a little preparation before navigation 42 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 43 | // Get the new view controller using [segue destinationViewController]. 44 | // Pass the selected object to the new view controller. 45 | } 46 | */ 47 | 48 | @end 49 | -------------------------------------------------------------------------------- /Example/RZColorful/Demo/RZTextViewVC.h: -------------------------------------------------------------------------------- 1 | // 2 | // RZTextViewVC.h 3 | // RZColorful_Example 4 | // 5 | // Created by rztime on 2022/1/26. 6 | // Copyright © 2022 rztime. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface RZTextViewVC : UIViewController 14 | 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /Example/RZColorful/Demo/RZTextViewVC.m: -------------------------------------------------------------------------------- 1 | // 2 | // RZTextViewVC.m 3 | // RZColorful_Example 4 | // 5 | // Created by rztime on 2022/1/26. 6 | // Copyright © 2022 rztime. All rights reserved. 7 | // 8 | 9 | #import "RZTextViewVC.h" 10 | #import 11 | #import 12 | 13 | @interface RZTextViewVC () 14 | 15 | @property (nonatomic, strong) UITextView *textView; 16 | 17 | @end 18 | 19 | @implementation RZTextViewVC 20 | 21 | - (void)viewDidLoad { 22 | [super viewDidLoad]; 23 | self.view.backgroundColor = UIColor.whiteColor; 24 | 25 | _textView = [[UITextView alloc] init]; 26 | [self.view addSubview:_textView]; 27 | [_textView mas_makeConstraints:^(MASConstraintMaker *make) { 28 | make.top.equalTo(self.view).offset(100); 29 | make.left.equalTo(self.view).offset(20); 30 | make.right.equalTo(self.view).offset((-20)); 31 | make.height.equalTo(@300); 32 | }]; 33 | _textView.backgroundColor = [UIColor colorWithWhite:0 alpha:0.1]; 34 | _textView.linkTextAttributes = @{}; 35 | 36 | [_textView rz_colorfulConfer:^(RZColorfulConferrer * _Nonnull confer) { 37 | confer.text(@"").font([UIFont systemFontOfSize:16]); 38 | 39 | confer.text(@"百年来,我们党团结带领人民进行的一切奋斗、一切牺牲、一切创造").font([UIFont systemFontOfSize:16]).textColor([UIColor blackColor]); 40 | confer.text(@"都是在践行为中国人民谋幸福、为中华民族谋复兴的初心使命\n中国共产党的成就和贡献,不仅是历史性的,也是世界性的").font([UIFont systemFontOfSize:16]).textColor([UIColor redColor]); 41 | confer.text(@"\n\n\n"); 42 | confer.text(@"添加链接").font([UIFont systemFontOfSize:18]).textColor([UIColor blueColor]).tapAction(@"点击事件"); 43 | }]; 44 | 45 | _textView.rzDidTapTextView = ^BOOL(id _Nullable tapObj) { 46 | NSString *obj = [((NSURL *)tapObj).absoluteString rz_decodedString]; 47 | NSLog(@"%@", obj); // http://www.baidu.com 48 | return false; 49 | }; 50 | } 51 | 52 | /* 53 | #pragma mark - Navigation 54 | 55 | // In a storyboard-based application, you will often want to do a little preparation before navigation 56 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 57 | // Get the new view controller using [segue destinationViewController]. 58 | // Pass the selected object to the new view controller. 59 | } 60 | */ 61 | 62 | @end 63 | -------------------------------------------------------------------------------- /Example/RZColorful/Images.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 | } 99 | -------------------------------------------------------------------------------- /Example/RZColorful/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Example/RZColorful/Images.xcassets/image.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "filename" : "image@3x.png", 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Example/RZColorful/Images.xcassets/image.imageset/image@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rztime/RZColorful/ab5ece13b6258431cccd1bb44f8d3350ac2af53e/Example/RZColorful/Images.xcassets/image.imageset/image@3x.png -------------------------------------------------------------------------------- /Example/RZColorful/RZAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // RZAppDelegate.h 3 | // RZColorful 4 | // 5 | // Created by rztime on 01/14/2022. 6 | // Copyright (c) 2022 rztime. All rights reserved. 7 | // 8 | 9 | @import UIKit; 10 | 11 | @interface RZAppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Example/RZColorful/RZAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // RZAppDelegate.m 3 | // RZColorful 4 | // 5 | // Created by rztime on 01/14/2022. 6 | // Copyright (c) 2022 rztime. All rights reserved. 7 | // 8 | 9 | #import "RZAppDelegate.h" 10 | #import "RZColorful.h" 11 | @implementation RZAppDelegate 12 | 13 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 14 | { 15 | // Override point for customization after application launch. 16 | [MarkdownRZ asyncInit:3]; 17 | return YES; 18 | } 19 | 20 | - (void)applicationWillResignActive:(UIApplication *)application 21 | { 22 | // 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. 23 | // 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. 24 | } 25 | 26 | - (void)applicationDidEnterBackground:(UIApplication *)application 27 | { 28 | // 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. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | - (void)applicationWillEnterForeground:(UIApplication *)application 33 | { 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 | { 39 | // 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. 40 | } 41 | 42 | - (void)applicationWillTerminate:(UIApplication *)application 43 | { 44 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 45 | } 46 | 47 | @end 48 | -------------------------------------------------------------------------------- /Example/RZColorful/RZColorful-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIMainStoryboardFile 30 | Main 31 | UIRequiredDeviceCapabilities 32 | 33 | armv7 34 | 35 | UISupportedInterfaceOrientations 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationLandscapeLeft 39 | UIInterfaceOrientationLandscapeRight 40 | 41 | UISupportedInterfaceOrientations~ipad 42 | 43 | UIInterfaceOrientationPortrait 44 | UIInterfaceOrientationPortraitUpsideDown 45 | UIInterfaceOrientationLandscapeLeft 46 | UIInterfaceOrientationLandscapeRight 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /Example/RZColorful/RZColorful-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #import 8 | 9 | #ifndef __IPHONE_5_0 10 | #warning "This project uses features only available in iOS SDK 5.0 and later." 11 | #endif 12 | 13 | #ifdef __OBJC__ 14 | @import UIKit; 15 | @import Foundation; 16 | #endif 17 | -------------------------------------------------------------------------------- /Example/RZColorful/RZViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // RZViewController.h 3 | // RZColorful 4 | // 5 | // Created by rztime on 01/14/2022. 6 | // Copyright (c) 2022 rztime. All rights reserved. 7 | // 8 | 9 | @import UIKit; 10 | 11 | @interface RZViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Example/RZColorful/RZViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // RZViewController.m 3 | // RZColorful 4 | // 5 | // Created by rztime on 01/14/2022. 6 | // Copyright (c) 2022 rztime. All rights reserved. 7 | // 8 | 9 | #import "RZViewController.h" 10 | #import 11 | #import 12 | 13 | @interface RZViewController () 14 | @property (nonatomic) UITableView *tableView; 15 | @property (nonatomic, strong) NSArray *items; 16 | @end 17 | 18 | @implementation RZViewController 19 | 20 | - (void)viewDidLoad 21 | { 22 | [super viewDidLoad]; 23 | // Do any additional setup after loading the view, typically from a nib. 24 | _tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain]; 25 | _tableView.delegate = self; 26 | _tableView.dataSource = self; 27 | [self.view addSubview:_tableView]; 28 | [_tableView mas_makeConstraints:^(MASConstraintMaker *make) { 29 | make.edges.equalTo(self.view); 30 | }]; 31 | _items = @[@{@"富文本常规使用": @"AttributedStringUseDemoVC"}, 32 | @{@"显示折叠展开的label": @"RZFoldLabelVC"}, 33 | @{@"UITextView常规使用": @"RZTextViewVC"}, 34 | @{@"测试": @"RZTestViewController"}, 35 | @{@"markdow to html": @"MDToHtmlViewController"}]; 36 | } 37 | 38 | - (void)didReceiveMemoryWarning 39 | { 40 | [super didReceiveMemoryWarning]; 41 | // Dispose of any resources that can be recreated. 42 | } 43 | 44 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 45 | return self.items.count; 46 | } 47 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 48 | NSDictionary *item = self.items[indexPath.row]; 49 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"]; 50 | if (cell == nil) { 51 | cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"cell"]; 52 | } 53 | cell.textLabel.text = item.allKeys.firstObject; 54 | cell.detailTextLabel.text = item.allValues.firstObject; 55 | return cell; 56 | } 57 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { 58 | return 70; 59 | } 60 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 61 | [tableView deselectRowAtIndexPath:indexPath animated:false]; 62 | NSDictionary *item = self.items[indexPath.row]; 63 | UIViewController *vc = (UIViewController *)[[NSClassFromString(item.allValues.firstObject) alloc] init]; 64 | [self.navigationController pushViewController:vc animated:true]; 65 | } 66 | 67 | @end 68 | -------------------------------------------------------------------------------- /Example/RZColorful/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Example/RZColorful/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // RZColorful 4 | // 5 | // Created by rztime on 01/14/2022. 6 | // Copyright (c) 2022 rztime. All rights reserved. 7 | // 8 | 9 | @import UIKit; 10 | #import "RZAppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) 13 | { 14 | @autoreleasepool { 15 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([RZAppDelegate class])); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Example/Tests/Tests-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 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Example/Tests/Tests-Prefix.pch: -------------------------------------------------------------------------------- 1 | // The contents of this file are implicitly included at the beginning of every test case source file. 2 | 3 | #ifdef __OBJC__ 4 | 5 | @import FBSnapshotTestCase; 6 | 7 | #endif 8 | -------------------------------------------------------------------------------- /Example/Tests/Tests.m: -------------------------------------------------------------------------------- 1 | // 2 | // RZColorfulTests.m 3 | // RZColorfulTests 4 | // 5 | // Created by rztime on 01/14/2022. 6 | // Copyright (c) 2022 rztime. All rights reserved. 7 | // 8 | 9 | @import XCTest; 10 | 11 | @interface Tests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation Tests 16 | 17 | - (void)setUp 18 | { 19 | [super setUp]; 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | } 22 | 23 | - (void)tearDown 24 | { 25 | // Put teardown code here. This method is called after the invocation of each test method in the class. 26 | [super tearDown]; 27 | } 28 | 29 | - (void)testExample 30 | { 31 | XCTFail(@"No implementation for \"%s\"", __PRETTY_FUNCTION__); 32 | } 33 | 34 | @end 35 | 36 | -------------------------------------------------------------------------------- /Example/Tests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2022 rztime 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RZColorful 2 | 3 | [![CI Status](https://img.shields.io/travis/rztime/RZColorful.svg?style=flat)](https://travis-ci.org/rztime/RZColorful) 4 | [![Version](https://img.shields.io/cocoapods/v/RZColorful.svg?style=flat)](https://cocoapods.org/pods/RZColorful) 5 | [![License](https://img.shields.io/cocoapods/l/RZColorful.svg?style=flat)](https://cocoapods.org/pods/RZColorful) 6 | [![Platform](https://img.shields.io/cocoapods/p/RZColorful.svg?style=flat)](https://cocoapods.org/pods/RZColorful) 7 | 8 | ## Example 9 | 10 | To run the example project, clone the repo, and run `pod install` from the Example directory first. 11 | 12 | ## Requirements 13 | 14 | ## Installation 15 | 16 | RZColorful is available through [CocoaPods](https://cocoapods.org). To install 17 | it, simply add the following line to your Podfile: 18 | 19 | ```ruby 20 | pod 'RZColorful' 21 | ``` 22 | 23 | ## Author 24 | 25 | rztime, rztime@vip.qq.com QQ群:580839749 26 | 27 | ## License 28 | 29 | RZColorful is available under the MIT license. See the LICENSE file for more info. 30 | 31 | 32 | # RZColorful 33 | iOS NSAttributedString 富文本方法 (图文混排、多样式文本); 文本超行,自动添加"折叠"、"全部"; markdown 转 html; 34 | 支持UILabel、UITextView、UITextField中可点击带链接的文本 35 | 36 | ## 更新日志 37 | [更新日志](https://github.com/rztime/RZColorful/blob/master/UpdateLog.md) 38 | 39 | swift版本[RZColorfulSwift](https://github.com/rztime/RZColorfulSwift) 40 | 41 | UITextView实现的富文本编辑器[RZRichTextView](https://github.com/rztime/RZRichTextView) 42 | 43 | * NSAttributedString 的多样化设置(文字字体、颜色、阴影、段落样式、url、下划线,以及图文混排等等) 44 | * 添加UITextField、UITextView、UILabel的attributedText的富文本设置。 45 | * 扩展:添加一个刷新界面时保持文本框焦点的方法 [demo查看](https://github.com/rztime/ContinueFirsterResponder) 46 | * 富文本方法内容可单独抽出来,在下边这个文件夹中 47 | ``` 48 | #import "NSAttributedString+RZColorful.h" 49 | ``` 50 | 51 | ## 关于RZColorful 52 | * 支持UILabel、UITextView、UITextField的attributedText的设置。 53 | * 支持获取NSAttributedString中的图片 54 | * 支持 HTML 与 NSAttributedString互换(支持图片) 55 | * 支持 markdown 文档转换为html 56 | * 包含的属性快捷设置: 57 | * 段落样式 58 | * 阴影 59 | * 文本字体、颜色 60 | * 文本所在区域对应的背景颜色 61 | * 连体字 62 | * 字间距 63 | * 删除线、下划线,及其线条颜色 64 | * 描边,及其颜色 65 | * 斜体字 66 | * 拉伸 67 | * 上下标 68 | * 书写方向(从左到右,从右到左) 69 | * 通过html源码加载富文本 70 | * 通过url添加图片到富文本 71 | 72 | ## How to use 73 | * 请在需要使用的地方加上 74 | 75 | ```objc 76 | #import "RZColorful.h" 77 | ``` 78 | 79 | * 主要的功能: 80 | * RZColorfulConferrer 81 | * text -- 添加文本 82 | * htmlText -- 添加html源码 83 | * image -- 添加图片 84 | * imageByUrl -- 添加图片(通过图片的URL添加) 85 | * paragraphStyle -- 全局的段落样式 86 | * shadow -- 全局的阴影样式 87 | 88 | * RZColorfulAttribute -- 设置文本的所有的属性 89 | * RZImageAttachment -- 设置图片的所有的属性 90 | 91 | ### 图示说明 92 | 93 |

94 | 95 |

96 | 97 | ### 讲解 98 | 99 | * `RZColorfulConferrer` 中 `paragraphStyle` 和 `shadow` 属于全局的设置,在block中任何位置都可设置 100 | * 文字和图片都可设置`paragraphStyle` 和 `shadow`,对齐方式是`paragraphStyle` 的`alignment`,设置后,当前行文字图片的全局样式将被此覆盖,即全局样式无效,不影响其他行。 101 | 102 | 103 | ### 基本的简单使用方法 104 | ```objc 105 | [cell.textLabel rz_colorfulConfer:^(RZColorfulConferrer * _Nonnull confer) { 106 | confer.paragraphStyle.paragraphSpacing(15); // 设置段落之间的行距 107 | 108 | confer.text(@"日常用处:(图片+标题+描述)\n").font(rzFont(17)).textColor([UIColor redColor]); 109 | 110 | confer.appendImage([UIImage imageNamed:@"test.jpg"]).bounds(CGRectMake(0, -2, 15, 15)); // 图片 111 | confer.text(@" 姓 名: ").font(rzFont(15)).textColor(RGBA(151, 151, 151, 11)); 112 | confer.text(@"rztime").font(rzFont(15)).textColor(RGBA(51, 51, 51, 1)); 113 | 114 | confer.text(@"\n"); 115 | confer.appendImage([UIImage imageNamed:@"test.jpg"]).bounds(CGRectMake(0, -2, 15, 15)); 116 | confer.text(@" 时 间: ").font(rzFont(15)).textColor(RGBA(151, 151, 151, 11)); 117 | confer.text([NSString stringWithFormat:@"%@", [NSDate new]]).font(rzFont(15)).textColor(RGBA(51, 51, 51, 1)); 118 | 119 | confer.text(@"\n"); 120 | confer.appendImage([UIImage imageNamed:@"test.jpg"]).bounds(CGRectMake(0, -2, 15, 15)); 121 | confer.text(@" 当次消费: ").font(rzFont(15)).textColor(RGBA(151, 151, 151, 11)); 122 | confer.text(@"¥").font(rzFont(15)).textColor(RGBA(51, 51, 51, 1)); 123 | confer.text(@"100").font(rzFont(15)).textColor(RGBA(251, 51, 51, 1)); 124 | confer.text(@"元").font(rzFont(15)).textColor(RGBA(51, 51, 51, 1)); 125 | }]; 126 | ``` 127 | 效果如下 128 |

129 | 130 |

131 | 132 | ### 段落样式、阴影(局部与全局统一的区别) 133 | 如果设置有局部样式,则全局样式无效 134 | ```objc 135 | [cell.textLabel rz_colorfulConfer:^(RZColorfulConferrer * _Nonnull confer) { 136 | confer.paragraphStyle.lineSpacing(5).paragraphSpacingBefore(5).alignment(NSTextAlignmentCenter); // 段落全局样式 137 | confer.shadow.color(RGBA(255, 0, 0, 0.3)).offset(CGSizeMake(1, 1)); // 阴影全局 138 | 139 | // 此部分显示全局样式的风格 (红色阴影,居中对齐,段落行距等) 140 | confer.appendImage([UIImage imageNamed:@"test.jpg"]).bounds(CGRectMake(0, -2, 15, 15)); 141 | confer.text(@" 姓名: ").font(rzFont(15)).textColor(RGBA(151, 151, 151, 11)); 142 | confer.text(@"rztime").font(rzFont(15)).textColor(RGBA(51, 51, 51, 1)); 143 | 144 | confer.text(@"\n"); 145 | confer.appendImage([UIImage imageNamed:@"test.jpg"]).bounds(CGRectMake(0, -2, 15, 15)); 146 | confer.text(@" 时间: ").font(rzFont(15)).textColor(RGBA(151, 151, 151, 11)); 147 | confer.text([NSString stringWithFormat:@"%@", [NSDate new]]).font(rzFont(15)).textColor(RGBA(51, 51, 51, 1)); 148 | 149 | // 此部分显示全局样式的风格 (居中对齐,段落行距等) 阴影将被局部覆盖(灰色) 150 | confer.text(@"\n地址: ").font(rzFont(15)).textColor(RGBA(151, 151, 151, 11)).paragraphStyle.paragraphSpacingBefore(20).and.shadow.color(GRAY(151)).offset(CGSizeMake(3, 3));; 151 | confer.text(@"成都-软件园").font(rzFont(15)).textColor(RGBA(51, 51, 51, 1)).shadow.color(GRAY(151)).offset(CGSizeMake(3, 3)); 152 | 153 | // 此部分段落样式被局部覆盖 阴影显示全局的 154 | confer.text(@"\n爱好: ").font(rzFont(15)).textColor(RGBA(151, 151, 151, 11)).paragraphStyle.paragraphSpacingBefore(20); 155 | confer.text(@"游山、").font(rzFont(15)).textColor(RGBA(151, 51, 51, 1)); 156 | confer.text(@"玩水、").font(rzFont(10)).textColor(RGBA(51, 151, 51, 1)); 157 | confer.text(@"听歌、").font(rzFont(18)).textColor(RGBA(51, 51, 151, 1)); 158 | confer.text(@"美食、").font(rzFont(17)).textColor(RGBA(51, 151, 51, 1)); 159 | confer.text(@"看电影、").font(rzFont(16)).textColor(RGBA(151, 51, 51, 1)); 160 | confer.text(@"撸代码、").font(rzFont(15)).textColor(RGBA(51, 151, 51, 1)); 161 | confer.text(@"等等\n\n").font(rzFont(15)).textColor(RGBA(251, 51, 51, 1)); 162 | }]; 163 | ``` 164 | 效果如下 165 |

166 | 167 |

168 | 169 | 通过url加载图片 170 | ```objc 171 | [cell.textLabel rz_colorfulConfer:^(RZColorfulConferrer * _Nonnull confer) { 172 | confer.appendImageByUrl(@"http://pic28.photophoto.cn/20130830/0005018667531249_b.jpg").bounds(CGRectMake(0, 0, 200, 0)).paragraphStyle.alignment(NSTextAlignmentLeft); // 宽或高为0时,即自动宽/高按照图片比例来 173 | }]; 174 | ``` 175 | 通过html源码加载文本 176 | ```objc 177 | [cell.textLabel rz_colorfulConfer:^(RZColorfulConferrer * _Nonnull confer) { 178 | NSString *resourcePath = [[NSBundle mainBundle] resourcePath]; 179 | NSString *filePath =[resourcePath stringByAppendingPathComponent:@"test.html"]; 180 | NSString *htmlstring=[[NSString alloc] initWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil]; 181 | confer.htmlText(htmlstring); 182 | }]; 183 | ``` 184 | 185 | 给UILabel添加富文本可点击功能 给文本添加tapActionByLable属性 186 | ```objc 187 | [_label rz_colorfulConfer:^(RZColorfulConferrer * _Nonnull confer) { 188 | confer.text(@"可点击的:").font([UIFont systemFontOfSize:17]).textColor(UIColor.blackColor); 189 | confer.text(@"文本").font([UIFont systemFontOfSize:17]).textColor(UIColor.redColor).tapActionByLable(@"1"); 190 | }]; 191 | [_label rz_tapAction:^(UILabel * _Nonnull label, NSString * _Nonnull tapActionId, NSRange range) { 192 | NSLog(@"%@", tapActionId); // print: 1 193 | }]; 194 | ``` 195 | 196 | 给UILabel添加超行之后的折叠、展开功能 197 | ```objc 198 | UILabel 199 | 200 | /// 设置富文本(超过行数后,自动追加“展开” “收起”) 201 | /// @param attr 原文 202 | /// @param line 最大显示行数 203 | /// @param width 最大显示宽度,这个宽度用于计算文本行 204 | /// @param fold 当前是否折叠 205 | /// @param allText 超过了行数之后,折叠状态显示的文本 如”展开“ 需要给文本设置NSTapActionByLabel属性 (tapActionByLable) 206 | /// @param foldText 超过行数之后,全部展开状态显示的文本 如”收起“ 需要给文本设置NSTapActionByLabel属性 (tapActionByLable) 207 | - (void)rz_setAttributedString:(NSAttributedString * _Nullable)attr maxLine:(NSInteger)line maxWidth:(CGFloat)width isFold:(BOOL)fold showAllText:(NSAttributedString *_Nullable)allText showFoldText:(NSAttributedString *_Nullable)foldText; 208 | ``` 209 | 210 | # 备注: 211 | * 多种属性使用名请参考对应的文件。 212 | * UILabel、UITextFile是同样的使用方法。 213 | * 在UILabel、UITextFiled上url点击方法无效。 214 | * 在UITextView中若要设置文本和图片的点击事件(即对文本和图片设置附带URL的属性),请先设置其editable = NO, 并实现代理。 215 | * 设置了URL属性的文本,内部自带蓝色和下划线,若要去掉,可设置 `textView.linkTextAttributes = @{};` 216 | 217 | ```objc 218 | // 实现下列方法 (二选一、或都可以实现,当都返回YES时,可能会打开safari浏览器) 219 | textView.rzDidTapTextView = ^BOOL(id _Nullable tapObj) { 220 | NSString *url = tapObj; 221 | if ([tapObj isKindOfClass:[NSURL class]]) { 222 | url = [(NSURL *)tapObj absoluteString]; 223 | } 224 | url = url.rz_decodedString; 225 | 226 | NSLog(@"rzDidTapTextView:tapObj:%@ \n如果url中包含了有中文,URL将会进行编码,所以请rz_decodedString解码之后查看:%@", tapObj, url); 227 | return NO; 228 | }; 229 | 230 | - (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange interaction:(UITextItemInteraction)interaction { 231 | NSLog(@"URL:%@", URL); 232 | // return NO;则不跳转,这里可以做一些基本判断在执行是否跳转浏览器打开url 233 | return YES; 234 | } 235 | ``` 236 | 237 | ## 注意 238 | 239 | * 尽管我已经在代码中已经处理过(弱)引用问题,但是在实际运用写入text时,还是请尽量检查避免循环引用 240 | 241 | 242 | ## 最后 243 | * 在使用过程中,如果您发现有什么问题,欢迎向我反馈,谢谢 244 | -------------------------------------------------------------------------------- /RZColorful.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod lib lint RZColorful.podspec' to ensure this is a 3 | # valid spec before submitting. 4 | # 5 | # Any lines starting with a # are optional, but their use is encouraged 6 | # To learn more about a Podspec see https://guides.cocoapods.org/syntax/podspec.html 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | s.name = 'RZColorful' 11 | s.version = '1.7.0' 12 | s.summary = 'NSAttributedString富文本的快捷设置方法集合,以及UITextView、UITextField、UILabel富文本简单优雅的使用, UILabel支持富文本点击,超行折叠、展开功能' 13 | 14 | # This description is used to generate tags and improve search results. 15 | # * Think: What does it do? Why did you write it? What is the focus? 16 | # * Try to keep it short, snappy and to the point. 17 | # * Write the description between the DESC delimiters below. 18 | # * Finally, don't worry about the indent, CocoaPods strips it! 19 | 20 | s.description = <<-DESC 21 | NSAttributedString 富文本方法 以及与html互相转换, UILabel的富文本点击事件 22 | DESC 23 | 24 | s.homepage = 'https://github.com/rztime/RZColorful' 25 | # s.screenshots = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2' 26 | s.license = { :type => 'MIT', :file => 'LICENSE' } 27 | s.author = { 'rztime' => 'rztime@vip.qq.com' } 28 | s.source = { :git => 'https://github.com/rztime/RZColorful.git', :tag => s.version.to_s } 29 | # s.social_media_url = 'https://twitter.com/' 30 | 31 | s.ios.deployment_target = '11.0' 32 | 33 | s.source_files = 'RZColorful/Classes/**/*' 34 | 35 | # s.resource_bundles = { 36 | # 'RZColorful' => ['RZColorful/Assets/*.png'] 37 | # } 38 | 39 | # s.public_header_files = 'Pod/Classes/**/*.h' 40 | # s.frameworks = 'UIKit', 'MapKit' 41 | # s.dependency 'AFNetworking', '~> 2.3' 42 | end 43 | -------------------------------------------------------------------------------- /RZColorful/Assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rztime/RZColorful/ab5ece13b6258431cccd1bb44f8d3350ac2af53e/RZColorful/Assets/.gitkeep -------------------------------------------------------------------------------- /RZColorful/Classes/AttributeCore/NSAttributedString+RZColorful.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSAttributedString+RZColorful.h 3 | // RZColorfulExample 4 | // 5 | // Created by rztime on 2017/11/15. 6 | // Copyright © 2017年 rztime. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "RZColorfulConferrer.h" 11 | #import "NSAttributedString+RZHtml.h" 12 | 13 | @class RZAttributedStringInfo; 14 | 15 | typedef NS_ENUM(NSInteger, RZAttributedStringAppendCondition) { 16 | RZAttributedStringAppendConditionLess = -1, // 比 小 17 | RZAttributedStringAppendConditionEqual = 0, // 相等 18 | RZAttributedStringAppendConditionMore = 1, // 比 大 19 | }; 20 | 21 | @interface NSAttributedString (RZColorful) 22 | /** 23 | 快捷创建富文本 24 | 25 | @param attribute <#attribute description#> 26 | @return <#return value description#> 27 | */ 28 | + (NSAttributedString *_Nullable)rz_colorfulConfer:(void(^_Nullable)(RZColorfulConferrer *_Nullable confer))attribute; 29 | // 追加 30 | - (NSAttributedString *_Nullable)attributedStringByAppend:(NSAttributedString *_Nullable)attributedString; 31 | // 获取富文本中的图片 用于上传服务器 32 | - (NSArray *_Nullable)rz_images; 33 | /** 34 | 固定宽度,计算高 35 | 36 | @param width 固定宽度 37 | @return <#return value description#> 38 | */ 39 | - (CGSize)sizeWithConditionWidth:(CGFloat)width; 40 | /** 41 | 固定高度,计算宽 42 | 43 | @param height 固定高度 44 | @return <#return value description#> 45 | */ 46 | - (CGSize)sizeWithConditionHeight:(CGFloat)height; 47 | 48 | #pragma mark - 其他方法 49 | /** 50 | 获取attrName对应的NSAttributedString 51 | */ 52 | - (NSArray *_Nullable)rz_attributedStringByAttributeName:(NSAttributedStringKey _Nonnull)attrName; 53 | 54 | /// 绘制在rect范围内时, 获取每一行的文本信息, 55 | /// @param rect 绘制范围 56 | - (NSArray *_Nullable)rz_linesIfDrawInRect:(CGRect)rect; 57 | 58 | /// 将attr追加到当前文本上 ,只有在rect上绘制,小于(等于或大于)line时,才将attr追加到self之后 ,如果将attr追加上去之后,行数大于line,则会对self的字符串截取 59 | /// @param attr 要追加的文本 60 | /// @param condition 满足的条件 61 | /// @param line 行数 62 | /// @param rect 要绘制的rect 63 | - (NSAttributedString *_Nullable)rz_appendAttributedString:(NSAttributedString *_Nullable)attr when:(RZAttributedStringAppendCondition)condition line:(NSInteger)line inRect:(CGRect)rect; 64 | @end 65 | 66 | @interface NSAttributedString (RZLines) 67 | 68 | /// 计算NSAttributedString的文本在width限制条件下, 是否超过line行 69 | - (BOOL)rz_moreThan:(NSInteger)line maxWidth:(CGFloat)width; 70 | 71 | /// 通过限制宽度,行数,获取内容, 72 | /// 如果未超过 line 行数,返回原字符串 73 | /// 超过行数,折叠时,将追加showAllText(如显示全文), 全部展开时,显示showFoldText(如收起) 74 | /// - Parameters: 75 | /// - maxline: 最大显示的行数, 76 | /// - maxWidth: 最大显示宽度 77 | /// - isFold: 当前是否折叠 78 | /// - showAllText: 如 “...显示全文” fold = true时,将追加在字符串后 79 | /// - showFoldText: 如 “收起全文” flod = FALSE,表示已全部展开,将追加在后边 80 | /// - Returns: 字符串 81 | - (NSAttributedString * _Nullable)rz_attributedStringBy:(NSInteger)maxLine maxWidth:(CGFloat)width isFold:(BOOL)fold showAllText:(NSAttributedString *_Nullable)allText showFoldText:(NSAttributedString *_Nullable)foldText; 82 | 83 | /// 对富文本进行截断处理 84 | /// - Parameters: 85 | /// - maxLine: 设置超过多少截断 86 | /// - width: 显示的最大宽度 87 | /// - model: 截断方式 88 | /// - placeHolder: 截断时占位的"..."文字 89 | - (NSAttributedString * _Nullable)rz_attributedStringBy:(NSInteger)maxLine maxWidth:(CGFloat)width lineBreakMode:(NSLineBreakMode)model placeHolder:(NSAttributedString *_Nullable)placeHolder; 90 | /// 头部截断(逆向)从最后一个字反向往前保留最后maxLine行数的文字 91 | /// - Parameters: 92 | /// - maxline: 最大行数 93 | /// - maxWidth: 最大宽度 94 | /// - lineBreakMode: placeholder插入的方式 95 | /// - placeHolder: 截断时占位的"..."文字 96 | /// - 区别于方法(A)的byTruncatingHead:func attributedStringBy(maxline: Int, maxWidth: CGFloat, lineBreakMode: NSLineBreakMode, placeHolder: NSAttributedString?) -> NSAttributedString? 97 | /// - 方法(A)抛弃前半部分,在最前边插入placeholder 98 | /// - headxxx抛弃前半部分,然后根据mode再不同位置插入placeholder 99 | - (NSAttributedString * _Nullable)rz_headTruncatingAttributedStringBy:(NSInteger)maxline maxWidth:(CGFloat)width lineBreakMode:(NSLineBreakMode)model placeHolder:(NSAttributedString *_Nullable)placeHolder; 100 | @end 101 | 102 | @interface NSAttributedString (RZAttr) 103 | /// 给text 关键字 标记属性(如对关键字进行标红显示) 104 | - (NSAttributedString *_Nonnull)rz_markText:(NSString *_Nullable)text attribute:(NSDictionary *_Nonnull)attribute; 105 | /// 取self的属性,设置在text上 106 | - (NSAttributedString *_Nonnull)rz_copyAttributeToText:(NSString *_Nonnull)text; 107 | 108 | @end 109 | 110 | // 要获取的attrName对应的信息 111 | @interface RZAttributedStringInfo : NSObject 112 | /** 对应的attributedString */ 113 | @property (nonatomic, strong) NSMutableAttributedString * _Nullable attributedString; 114 | /** 所在的range */ 115 | @property (nonatomic, assign) NSRange range; 116 | /** 对应可以的值 */ 117 | @property (nonatomic, strong) id _Nullable value; 118 | /** 所要查询的attrName */ 119 | @property (nonatomic, copy) NSAttributedStringKey _Nullable attrName; 120 | 121 | @end 122 | 123 | -------------------------------------------------------------------------------- /RZColorful/Classes/AttributeCore/NSAttributedString+RZColorful.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSAttributedString+RZColorful.m 3 | // RZColorfulExample 4 | // 5 | // Created by rztime on 2017/11/15. 6 | // Copyright © 2017年 rztime. All rights reserved. 7 | // 8 | 9 | #import "NSAttributedString+RZColorful.h" 10 | #import 11 | #import 12 | #import "NSAttributedString+RZHtml.h" 13 | 14 | #pragma clang diagnostic push 15 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" 16 | 17 | @implementation NSAttributedString (RZColorful) 18 | 19 | + (NSAttributedString *)rz_colorfulConfer:(void(^)(RZColorfulConferrer *confer))attribute { 20 | if(!attribute) { 21 | return [NSAttributedString new]; 22 | } 23 | RZColorfulConferrer *conferrer = [[RZColorfulConferrer alloc]init]; 24 | attribute(conferrer); 25 | NSAttributedString *attr = [conferrer confer]; 26 | return attr; 27 | } 28 | 29 | - (NSAttributedString *)attributedStringByAppend:(NSAttributedString *)attributedString { 30 | NSMutableAttributedString *attr = [[NSMutableAttributedString alloc] initWithAttributedString:self]; 31 | [attr appendAttributedString:attributedString]; 32 | return attr.copy; 33 | } 34 | 35 | /** 36 | 固定宽度,计算高 37 | 38 | @param width 固定宽度 39 | @return <#return value description#> 40 | */ 41 | - (CGSize)sizeWithConditionWidth:(CGFloat)width { 42 | CGSize size = [self sizeWithCondition:CGSizeMake(width, CGFLOAT_MAX)]; 43 | size.width = width; 44 | return size; 45 | } 46 | 47 | 48 | /** 49 | 固定高度,计算宽 50 | 51 | @param height 固定高度 52 | @return <#return value description#> 53 | */ 54 | - (CGSize)sizeWithConditionHeight:(CGFloat)height { 55 | CGSize size = [self sizeWithCondition:CGSizeMake(CGFLOAT_MAX, height)]; 56 | size.height = height; 57 | return size; 58 | } 59 | /** 60 | 计算富文本的size 61 | 如需计算高度 size = CGSizeMake(300, CGFLOAT_MAX) 62 | 如需计算宽度 size = CGSizeMake(CGFLOAT_MAX, 18) 63 | 64 | @param size 约定的size,以宽高做条件,定宽时,计算得到高度(此时忽略宽度) 65 | 定高时,计算得到宽度 (此时忽略高度) 66 | @return <#return value description#> 67 | */ 68 | - (CGSize)sizeWithCondition:(CGSize)size { 69 | CGRect rect = [self boundingRectWithSize:size options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading context:nil]; 70 | return rect.size; 71 | } 72 | 73 | 74 | // 获取富文本中的图片 75 | - (NSArray *)rz_images { 76 | NSMutableArray *arrays = [NSMutableArray new]; 77 | [self enumerateAttribute:NSAttachmentAttributeName inRange:NSMakeRange(0, self.length) options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired usingBlock:^(id _Nullable value, NSRange range, BOOL * _Nonnull stop) { 78 | if ([value isKindOfClass:[NSTextAttachment class]]) { 79 | NSTextAttachment *imageMent = value; 80 | if (imageMent.image) { 81 | [arrays addObject:imageMent.image]; 82 | } else if(imageMent.fileWrapper.regularFileContents) { 83 | UIImage *image = [UIImage imageWithData:imageMent.fileWrapper.regularFileContents]; 84 | if (image) { 85 | [arrays addObject:image]; 86 | } 87 | } 88 | } 89 | }]; 90 | return arrays.copy; 91 | } 92 | 93 | 94 | - (NSArray *)rz_attributedStringByAttributeName:(NSAttributedStringKey)attrName { 95 | if (self.length == 0) { 96 | return nil; 97 | } 98 | NSMutableArray *attris = [NSMutableArray new]; 99 | __weak typeof (self) weakSelf = self; 100 | [self enumerateAttribute:attrName inRange:NSMakeRange(0, self.length) options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired usingBlock:^(id _Nullable value, NSRange range, BOOL * _Nonnull stop) { 101 | if (value) { 102 | RZAttributedStringInfo *info = [RZAttributedStringInfo new]; 103 | info.attributedString = [weakSelf attributedSubstringFromRange:range].mutableCopy; 104 | info.range = range; 105 | info.value = value; 106 | info.attrName = attrName; 107 | [attris addObject:info]; 108 | } 109 | }]; 110 | return attris; 111 | } 112 | 113 | /// 绘制在rect范围内时, 获取每一行的文本信息, 114 | /// @param rect 绘制范围 115 | - (NSArray *)rz_linesIfDrawInRect:(CGRect)rect { 116 | CTFramesetterRef frameSetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)self); 117 | CGMutablePathRef path = CGPathCreateMutable(); 118 | CGPathAddRect(path, NULL, rect); 119 | CTFrameRef frame = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, 0), path, NULL); 120 | NSArray *lines = (__bridge NSArray *)CTFrameGetLines(frame); 121 | 122 | NSMutableArray *infos = [NSMutableArray new]; 123 | for (id line in lines) { 124 | CTLineRef lineRef = (__bridge CTLineRef )line; 125 | CFRange lineRange = CTLineGetStringRange(lineRef); 126 | NSRange range = NSMakeRange(lineRange.location, lineRange.length); 127 | 128 | RZAttributedStringInfo *info = [RZAttributedStringInfo new]; 129 | info.attributedString = [self attributedSubstringFromRange:range].mutableCopy; 130 | info.range = range; 131 | [infos addObject:info]; 132 | } 133 | return infos.copy; 134 | } 135 | 136 | /// 将attr追加到当前文本上 ,只有在rect上绘制,小于(等于或大于)line时,才将attr追加到self之后 137 | /// @param attr 要追加的文本 138 | /// @param condition 满足的条件 139 | /// @param line 行数 140 | /// @param rect 要绘制的rect 141 | - (NSAttributedString *)rz_appendAttributedString:(NSAttributedString *)attr when:(RZAttributedStringAppendCondition)condition line:(NSInteger)line inRect:(CGRect)rect { 142 | if (!attr) { 143 | attr = [NSAttributedString new]; 144 | } 145 | NSMutableAttributedString *tempAttr = self.mutableCopy; 146 | NSArray *lines = [tempAttr rz_linesIfDrawInRect:rect]; 147 | NSComparisonResult result = [@(lines.count) compare:@(line)]; 148 | if ((RZAttributedStringAppendCondition)result == condition) { 149 | for (NSInteger lineNum = MIN(lines.count, line) - 1; lineNum >= 0; lineNum--) { 150 | BOOL flag = NO; 151 | RZAttributedStringInfo *lastLineInfo = lines[lineNum]; 152 | for (NSInteger row = lastLineInfo.range.length; row >= 0; row--) { 153 | NSInteger length = lastLineInfo.range.location + row; 154 | 155 | NSMutableAttributedString *temp = [tempAttr attributedSubstringFromRange:NSMakeRange(0, length)].mutableCopy; 156 | [temp appendAttributedString:attr]; 157 | NSArray *tempLines = [temp rz_linesIfDrawInRect:rect]; 158 | if (tempLines.count <= line) { 159 | RZAttributedStringInfo *tempLast = tempLines.lastObject; 160 | CGSize size = [tempLast.attributedString sizeWithConditionHeight:CGFLOAT_MAX]; 161 | if (size.width <= rect.size.width) { 162 | flag = YES; 163 | 164 | if ([temp.string hasSuffix:@"\n"]) { 165 | [temp replaceCharactersInRange:NSMakeRange(temp.length - 1, 1) withString:@""]; 166 | } 167 | tempAttr = temp.copy; 168 | break; 169 | } 170 | } 171 | } 172 | if (flag) { 173 | break; 174 | } 175 | } 176 | } 177 | return tempAttr.copy; 178 | } 179 | 180 | @end 181 | 182 | @implementation NSAttributedString(RZLines) 183 | 184 | - (BOOL)rz_moreThan:(NSInteger)line maxWidth:(CGFloat)width { 185 | UILabel *label = [[UILabel alloc] init]; 186 | label.numberOfLines = 0; 187 | label.attributedText = self; 188 | CGSize tempSize = CGSizeMake(width, CGFLOAT_MAX); 189 | CGFloat allHeight = [label textRectForBounds:CGRectMake(0, 0, tempSize.width, tempSize.height) limitedToNumberOfLines:0].size.height; 190 | CGFloat lineHeight = [label textRectForBounds:CGRectMake(0, 0, tempSize.width, tempSize.height) limitedToNumberOfLines:line].size.height; 191 | return ceil(allHeight) > ceil(lineHeight); 192 | } 193 | 194 | - (NSAttributedString *)rz_attributedStringBy:(NSInteger)maxLine maxWidth:(CGFloat)width isFold:(BOOL)fold showAllText:(NSAttributedString *)allText showFoldText:(NSAttributedString *)foldText { 195 | if (self.length == 0 || maxLine == 0 || width == 0) { 196 | return self; 197 | } 198 | if (![self rz_moreThan:maxLine maxWidth:width]) { 199 | return self; 200 | } 201 | if (!fold) { 202 | NSMutableAttributedString *attr = [[NSMutableAttributedString alloc] initWithAttributedString:self]; 203 | if (foldText != nil) { 204 | [attr appendAttributedString:foldText]; 205 | } 206 | return attr; 207 | } 208 | return [self rz_attributedStringBy:maxLine maxWidth:width lineBreakMode:NSLineBreakByTruncatingTail placeHolder:allText]; 209 | } 210 | /// 对富文本进行截断处理 211 | /// - Parameters: 212 | /// - maxLine: 设置超过多少截断 213 | /// - width: 显示的最大宽度 214 | /// - model: 截断方式 215 | /// - placeHolder: 截断时占位的"..."文字 216 | - (NSAttributedString * _Nullable)rz_attributedStringBy:(NSInteger)maxLine maxWidth:(CGFloat)width lineBreakMode:(NSLineBreakMode)model placeHolder:(NSAttributedString *_Nullable)placeHolder { 217 | if (self.length == 0 || maxLine == 0 || width == 0) { 218 | return self; 219 | } 220 | if (![self rz_moreThan:maxLine maxWidth:width]) { 221 | return self; 222 | } 223 | NSAttributedString *holder = placeHolder == nil ? [NSAttributedString new] : placeHolder; 224 | NSInteger left = 0; 225 | NSInteger right = self.length; 226 | NSInteger location = 0; 227 | NSAttributedString *resultAttr; 228 | switch (model) { 229 | case NSLineBreakByWordWrapping: 230 | case NSLineBreakByCharWrapping: 231 | case NSLineBreakByClipping: 232 | case NSLineBreakByTruncatingTail: 233 | while (left <= right) { 234 | location = (left + right) / 2; 235 | NSAttributedString *sub = [self attributedSubstringFromRange:NSMakeRange(0, location)]; 236 | NSMutableAttributedString *temp = sub.mutableCopy; 237 | if (model == NSLineBreakByTruncatingTail) { 238 | [temp appendAttributedString:holder]; 239 | } 240 | BOOL more = [temp rz_moreThan:maxLine maxWidth:width]; 241 | if (more) { 242 | right = location - 1; 243 | } else { 244 | left = location + 1; 245 | resultAttr = temp; 246 | } 247 | } 248 | break; 249 | case NSLineBreakByTruncatingHead: 250 | while (left <= right) { 251 | location = (left + right) / 2; 252 | NSAttributedString *sub = [self attributedSubstringFromRange:NSMakeRange(location, self.length - location)]; 253 | NSMutableAttributedString *temp = sub.mutableCopy; 254 | [temp insertAttributedString:holder atIndex:0]; 255 | BOOL more = [temp rz_moreThan:maxLine maxWidth:width]; 256 | if (more) { 257 | left = location + 1; 258 | } else { 259 | right = location - 1; 260 | resultAttr = temp; 261 | } 262 | } 263 | break; 264 | case NSLineBreakByTruncatingMiddle: { 265 | // 将富文本分为左右两部分,一左一右交替二分查找获取截断位置 266 | // 当maxline = 1行时,效果不太理想(有好的方法可以反馈一下) 267 | /// 左边 268 | NSInteger left_l = 0; 269 | NSInteger left_r = self.length / 2; 270 | 271 | /// 右边 272 | NSInteger right_l = self.length / 2; 273 | NSInteger right_r = self.length; 274 | 275 | /// 左右mid 276 | NSInteger left_mid = 0; 277 | NSInteger right_mid = right_l; 278 | 279 | /// 每次计算,双数时计算左侧,单数时计算右侧 280 | /// 优先截断左侧,当一侧截断位置后未超行,times不变,继续算这一侧,直到这一侧超行之后,在截取另一侧 281 | /// 即超行之后,才times+=1, 282 | /// 如times=双数,即左侧,当次未超行时times不变,下一次循环依然会截断左侧,直到超行时,才去截断右侧 283 | NSInteger times = 0; 284 | while (left_l <= left_r || right_l <= right_r) { 285 | NSMutableAttributedString *temp = [[NSMutableAttributedString alloc] init]; 286 | 287 | // 交替计算左侧和右侧的中间位置 288 | if (times % 2 == 0) { /// 算左侧 289 | left_mid = (left_l + left_r) / 2; 290 | } else { 291 | right_mid = (right_l + right_r) / 2; 292 | } 293 | 294 | NSAttributedString *h = [self attributedSubstringFromRange:NSMakeRange(0, left_mid)]; 295 | NSAttributedString *e = [self attributedSubstringFromRange:NSMakeRange(right_mid, self.length - right_mid)]; 296 | 297 | [temp appendAttributedString:h]; 298 | [temp appendAttributedString:holder]; 299 | [temp appendAttributedString:e]; 300 | 301 | if ([temp rz_moreThan:maxLine maxWidth:width]) { 302 | if (times % 2 == 0) { /// 算左侧 303 | left_r = left_mid - 1; 304 | } else { 305 | right_l = right_mid + 1; 306 | } 307 | times += 1; 308 | } else { 309 | if (times % 2 == 0) { /// 算左侧 310 | left_l = left_mid + 1; 311 | } else { 312 | right_r = right_mid - 1; 313 | } 314 | 315 | /// 左侧已到临界点,则重置times仅算右侧,同理右侧临界点 316 | if (left_l > left_r) { 317 | times = 1; 318 | } else if (right_l > right_r) { 319 | times = 0; 320 | } 321 | resultAttr = temp; 322 | } 323 | } 324 | break; 325 | } 326 | default: 327 | return self; 328 | } 329 | return resultAttr; 330 | } 331 | /// 头部截断(逆向)从最后一个字反向往前保留最后maxLine行数的文字 332 | /// - Parameters: 333 | /// - maxLine: 设置超过多少截断 334 | /// - width: 显示的最大宽度 335 | /// - model: 截断方式 336 | /// - placeHolder: 截断时占位的"..."文字 337 | - (NSAttributedString * _Nullable)rz_headTruncatingAttributedStringBy:(NSInteger)maxLine maxWidth:(CGFloat)width lineBreakMode:(NSLineBreakMode)model placeHolder:(NSAttributedString *_Nullable)placeHolder { 338 | if (self.length == 0 || maxLine == 0 || width == 0) { 339 | return self; 340 | } 341 | if (![self rz_moreThan:maxLine maxWidth:width]) { 342 | return self; 343 | } 344 | NSAttributedString *holder = placeHolder == nil ? [NSAttributedString new] : placeHolder; 345 | NSInteger min = 0; 346 | NSInteger max = self.length; 347 | NSInteger star = 0; 348 | NSAttributedString *resultAttr; 349 | while (min <= max) { 350 | star = (min + max) / 2; 351 | NSAttributedString *sub = [self attributedSubstringFromRange:NSMakeRange(star, self.length - star)]; 352 | NSMutableAttributedString *tempAttr = [[NSMutableAttributedString alloc] initWithAttributedString:sub]; 353 | switch (model) { 354 | case NSLineBreakByWordWrapping: { break; } 355 | case NSLineBreakByCharWrapping: { break; } 356 | case NSLineBreakByClipping: { break; } 357 | case NSLineBreakByTruncatingHead: { 358 | [tempAttr insertAttributedString:holder atIndex:0]; 359 | break; 360 | } 361 | case NSLineBreakByTruncatingTail: { 362 | [tempAttr appendAttributedString:holder]; 363 | break; 364 | } 365 | case NSLineBreakByTruncatingMiddle: { 366 | [tempAttr insertAttributedString:holder atIndex:(tempAttr.length / 2)]; 367 | break; 368 | } 369 | default: 370 | break; 371 | } 372 | BOOL more = [tempAttr rz_moreThan:maxLine maxWidth:width]; 373 | if (more) { 374 | min = star + 1; 375 | } else { 376 | max = star - 1; 377 | resultAttr = tempAttr; 378 | } 379 | } 380 | return resultAttr; 381 | } 382 | /// 给text 关键字 标记属性(如对关键字进行标红显示) 383 | - (NSAttributedString *_Nonnull)rz_markText:(NSString *_Nullable)text attribute:(NSDictionary *_Nonnull)attribute { 384 | if ((text == nil) || (text.length == 0)) { 385 | return self; 386 | } 387 | NSMutableAttributedString *attr = [[NSMutableAttributedString alloc] initWithAttributedString:self]; 388 | NSString *t = self.string; 389 | NSRange range = NSMakeRange(0, t.length); 390 | while (range.length > 0) { 391 | NSRange r = [t rangeOfString:text options:NSCaseInsensitiveSearch range:range]; 392 | if (r.location != NSNotFound) { 393 | [attr addAttributes:attribute range:r]; 394 | NSInteger location = r.location + r.length; 395 | range = NSMakeRange(location, (t.length - location)); 396 | } else { 397 | break; 398 | } 399 | } 400 | return attr; 401 | } 402 | /// 取self的属性,设置在text上 403 | - (NSAttributedString *_Nonnull)rz_copyAttributeToText:(NSString *_Nonnull)text { 404 | NSDictionary *dict = [self attributesAtIndex:0 effectiveRange:nil]; 405 | if (!dict) { 406 | dict = NSDictionary.new; 407 | } 408 | return [[NSAttributedString alloc] initWithString:text attributes:dict]; 409 | } 410 | 411 | @end 412 | 413 | @implementation RZAttributedStringInfo 414 | 415 | @end 416 | 417 | 418 | #pragma clang diagnostic pop 419 | -------------------------------------------------------------------------------- /RZColorful/Classes/AttributeCore/NSAttributedString+RZHtml.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSAttributedString+RZHtml.h 3 | // RZColorfulExample 4 | // 5 | // Created by rztime on 2020/8/12. 6 | // Copyright © 2020 rztime. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "NSAttributedString+RZColorful.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface NSAttributedString (RZHtml) 15 | // 将html转换成 NSAttributedString 16 | + (NSAttributedString *)htmlString:(NSString *)html; 17 | /** 18 | 将富文本完整的code成html标签,(此方法如果富文本中有图片,则图片将被丢失) 有图片时,请用[rz_codingToHtmlWithImagesURLSIfHad]方法 (备注 使用系统方法,某些标签将不能转换) 19 | @return HTML标签语言 20 | */ 21 | - (NSString *)rz_codingToCompleteHtml; 22 | 23 | /// 将富文本转换为web可用的html 24 | /// 备注 :1将系统不能转换的标签,通过外部方法写入style,转换成web可用的html 25 | /// 2 通过htmlString还原成NSAttributedString,部分属性也有可能丢失 如下划线删除线的颜色(转换时,系统取得是