富文本
iOS系统优雅的UI交互界面是我们使用iPhone的理由之一,这当然少不了其中优美的多信息文本了。多信息文本也被称为富文本,在Cocoa里也被称为AttributedText。
富文本特别的地方,在于其表现了纯文本所无法表现出来的信息,这些信息可以代表文本所表达出来的感情,比起单纯的通过文字内容来表达感情,这种感情很显然具有强烈的冲击力。
“喜悦”一词,可以像纯文本这般干巴巴,也可以像下图艺术字那般喜气洋洋,这一切都是富文本所能表达出来的强烈感情。
当然了,上面的“喜悦”,更多是偏向艺术方面的表达,我们所要说的富文本,虽然也包含了这方面,却不止于此。
编程界的富文本,不仅仅能够表达感情,还能表达出常规艺术字所无法表达出来的信息,比如:
超链接:让你的文本能够产生交互
段落编排:定义文字的格局,让格局符合你所需要的结构,以方便读者的阅读
身份:粗体作为标题,灰色字体作为提示,常规体作为正文等,在许多开发案例之中,我们往往需要用到这种身份标志
······
富文本的编程方式
作为iOS程序员,我们在代码里该怎么创造富文本呢?一种方式是通过苹果公司的官方API来编写富文本代码,另一种方式则是通过第三方库,笔者想其中最具代表性的应该是YYText了。
通过官方API
通过官方的API,大家马上想到的便是NSAttributedString了。它是基于TextKit的一种相对简单的富文本API,相比定制能力更高的TextKit,其用法要简单不少,且满足绝大部分开发需求。
假如我们需要实现这样一个富文本:
通过NSAttributedString大体可以这么做:
UITextView *textView= [UITextView new];
NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithString:@"this is a BlueLink,and this is a RedLink"];
[string addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:14] range:NSMakeRange(0, 10)];
[string addAttribute:NSForegroundColorAttributeName value:[UIColor blackColor] range:NSMakeRange(0, 10)];
[string addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:17] range:NSMakeRange(10, 8)];
[string addAttribute:NSForegroundColorAttributeName value:[UIColor blueColor] range:NSMakeRange(10, 8)];
[string addAttribute:NSLinkAttributeName value:@"https://www.jianshu.com" range:NSMakeRange(10, 8)];
[string addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:14] range:NSMakeRange(18, 13)];
[string addAttribute:NSForegroundColorAttributeName value:[UIColor blackColor] range:NSMakeRange(18, 13)];
[string addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:17] range:NSMakeRange(33, 7)];
[string addAttribute:NSLinkAttributeName value:@"https://www.jianshu.com" range:NSMakeRange(33, 7)];
[string addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:NSMakeRange(33, 7)];
textView.linkTextAttributes = @{};
textView.attributedText = string;
如此之多的代码量,只是因为一段小小的样式,写起来竟如此麻烦,不仅无法轻易从代码读出其意图,还冗长无比,更甚者,如果你想自己处理链接的回调,还得自己实现UITextView的相关委托。
事实上,相比更底层的API如TextKit、CoreText,NSAttributedString已经相当之简单了,若要是在更早之前,iOS开发者们只能面对CoreText这种相当底层的API,那个时候的富文本实现难度非常之高。尽管如此,NSAttributedString用起来还是不太尽如人意。
通过YYText
那么使用YYText会怎么实现?看过其源代码都能知道,该作者相当厉害。但是通过其Github文档了解到,其编程方式有所改善,代码量减少了,易读性稍微加强了一些,但是在编程方式上却未有太大改变,写起来的感觉其实还是如同上面那样别扭。具体请了解 https://github.com/ibireme/YYText。
通过一种新的方式 NudeIn
如果你用过Masonry,你一定会被其独特的编程方式所吸引。Masonry在手写约束代码上完全让我们摆脱了原生约束代码令人抓狂的编程方式,Masonry是按照人的思维进行设计的。换句话说,你脑海里第一时间所想的,就是Masonry如何进行的。
笔者自身也是Masonry的忠实用户,收到其灵感的激发,笔者重新设计了一下富文本的编程方式 NudeIn。先不说别的,让我们来看看上面那个例子使用这种新的方式应该如何实现。
使用 NudeIn,我们可以这么做:
NudeIn *nude = [NudeIn make:^(NUDTextMaker *make) {
make.text(@"this is a ").font(14).color([UIColor blackColor]).attach();
make.text(@"BlueLink").font(17).color([UIColor blueColor]).link(self,@selector(linkHandler:)).attach();
make.text(@", and this is a ").font(14).color([UIColor blackColor]).attach();
make.text(@"RedLink").font(17).color([UIColor redColor]).link(self,@selector(linkHandler:)).attach();
}];
实现回调 linkHandler
- (void)linkHandler:(NUDAction *)action {
if ([action isKindOfClass:[NUDLinkAction class]]) {
NUDLinkAction *linkAction = (NUDLinkAction *)action;
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:linkAction.string message:nil preferredStyle:UIAlertControllerStyleAlert];
[alertController addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
}]];
[self presentViewController:alertController animated:YES completion:nil];
}
}
代替冗长难以阅读的原生代码,笔者将不同属性的文字拆分成了一个个组件,每个组件通过类似Masonry的方式来定义自己本身的属性。NudeIn 让你可以轻松将一个复杂的富文本标签按照顺序拆分成各种不同的控件,每个控件可以集成任何预设的属性。
当然,冗余还是有的,如某些案例里,有大量不同属性的文字也会拥有许多相似点,如仅仅是颜色不一样的话,按照上面的编程方式,将会有许多冗余代码。
为了减少这种冗余,笔者引入了 Template,通过Template,你可以将消除这种冗余:
NudeIn *nude = [NudeIn make:^(NUDTextMaker *make) {
make.textTemplate(@"normal").font(14).color([UIColor blackColor]).attach();
make.textTemplate(@"highlight").font(17).color([UIColor blueColor]).attach();
make.text(@"this is a ").nud_attachWith(@"normal");
make.text(@"BlueLink").nud_attachWith(@"highlight");
make.text(@", and this is a ").nud_attachWith(@"normal");
make.text(@"RedLink").color([UIColor redColor]).nud_attachWith(@"highlight");
}];
上面的例子,笔者定义了两个模板,一个 normal 模板代表常规字体,一个 highlight 模板代表高亮字体,且颜色默认为蓝色。特别的,对于 “RedLink” 这个组件,相比BlueLink有一些差异,无需担心,模板里的任何属性都是可以覆盖。笔者就通过重新定义它的颜色属性来覆盖原有模板上的颜色属性。
通过这种方式,对于复杂度不高的富文本来说并没有什么必要,但是对于高复杂度文本来说,它大大减少了冗余代码,效果会非常好,在易读性和易写性上也没减弱的地方。
不仅仅是template,你还可以为所有组件定义通用的属性:
NudeIn *nude = [NudeIn make:^(NUDTextMaker *make) {
make.allText().font(14).color([UIColor redColor]).attach();
make.text(@"").attach();
.....
}];
这样,所有的组件都将是字号14,颜色为红色的组件。
除了常规文本的标签,NudeIn 还可以非常方便的为文字添加任何图片,如表情符号这种。
NudeIn *nude = [NudeIn make:^(NUDTextMaker *make) {
make.image(@"image").origin(0,0).size(0,0).vertical(1).attach();
make.text(@"text").attach();
.....
}];
这样,image 图标就会挂在text文字前面。
如果你想,你还可以通过 origin 和 size 来定义它的rect,通过 vertical 来调整它的垂直升降。
特别的,image 默认使用 [UIImage imageName:]来获取图片,如果你想使用自己生成的图片,可以通过 imageRes 标签,传入UIImage 对象。
你甚至还可以通过 imageTemplate 定义 image 的模板,其用法就像文字组件一样,没有任何区别。
在笔者的工作里,笔者也引入了这款富文本库进行使用,在使用的过程中笔者发现自己越来越依赖这款控件了,有时候就算不是富文本,也感觉使用 NudeIn 会比使用传统的 UILabel 要来得舒服,不知道是不是有点问题呢,
NudeIn 目前还在完善之中,文档相对也不是太完善,但是笔者认为它非常棒,当前已经引入 highlight 功能,并打算加入 press 和 longPress 属性来代替无法自定义高亮的 link 属性
NudeIn 地址:https://github.com/hon-key/Nudeln