如果遇到上面一个需求,你会怎么处理,若干个UILabel+UIImageView?NSAttributedString拼接?CoreText?
我相信不论是哪种方式代码量都不小,并且难以复用,其他语言写富文本是那么轻松,Android天生支持简单HTML,RN(JS)标签套标签,而只要用过iOS中的富文本都会觉得难用...目前业界功能强大、较为好用的是YYText,但设计思想是尽可能与UILabel、UITextView相似,所以相对使用也不是特别简单,而且框架较重。
基于现状开发了一套轻量的框架 ZJAttributedText,ZJAttributedText是高性能轻量级富文本框架,满足大部分富文本需求,并且提供了手势响应、绘制回调、图文对齐、CoreText属性扩展、支持网络图片、异步绘制性能优化,最重要的是使用简单,通过链式语法轻松写出一篇图文混排文本.
示例说明如图所示一篇图文混排,涉及到字体,颜色,字间距,行间距,图片对齐,文字对齐,描边等等属性,还有网络图片与本地图片混排,手势响应等需求,使用本框架可以下面这样实现:
//...省略常量声明 //标题 title.font(titleFont).color(titleColor).onClicked(titleOnClicked).onLayout(titleOnLayout); //首段 firstPara.color(firstParaColor).align(@0); //图片需要用一个空字符串起头 NSString *webImageString = @"".append(webImageURL).font(separateLineFont).minLineHeight(@100); //分割线 separateLine.font(separateLineFont).strokeColor(separateLineColor).strokeWidth(@1); //本地图片 NSString *locolImageString = @"".append(locolImage); //最后一段 lastPara.font(lastParaFont).align(@1); //书名 bookName.font(bookNameFont).color(bookNameColor).onClicked(bookOnClicked).align(@1); //引用 quote.color(quoteColor).letterSpace(@0).minLineSpace(@8).align(@0); //设置全局默认属性, 优先级低于指定属性 NSString *defaultAttributes = @"".entire() .maxSize(maxSize).align(@2).letterSpace(@3).minLineHeight(@20).maxLineHeight(@20).imageAlign(@1).onClicked(textOnClicked).imageSize(imageSize); //拼接 title.append(firstPara).append(webImageString).append(separateLine).append(locolImageString).append(lastPara).append(bookName).append(quote) //设置默认属性 .append(defaultAttributes) //绘制View .drawView(^(UIView *drawView) { [self.view addSubview:drawView]; });甚至可以这样实现:
//...省略常量声明 @"" //拼接全文 .append(title).font(titleFont).color(titleColor).onClicked(titleOnClicked).onLayout(titleOnLayout) .append(firstPara).color(firstParaColor).align(@0) .append(webImageURL).font(separateLineFont).minLineHeight(@100) .append(separateLine).font(separateLineFont).strokeColor(separateLineColor).strokeWidth(@1) .append(locolImage) .append(lastPara).font(lastParaFont).align(@1) .append(bookName).font(bookNameFont).color(bookNameColor).onClicked(bookOnClicked).align(@1) .append(quote).color(quoteColor).letterSpace(@0).minLineSpace(@8).align(@0) //设置默认属性 .entire().maxSize(maxSize).align(@2).letterSpace(@3).minLineHeight(@20).maxLineHeight(@20).imageAlign(@1).onClicked(textOnClicked).imageSize(imageSize) //绘制 .drawView(^(UIView *drawView) { [self.view addSubview:drawView]; });核心方法与属性对NSString的扩展
核心方法
append(idcontent)
拼接content 可以是文本(NSString)、图片(UIImage)、图片链接(NSURL)(必须指定imageSize属性)、视图(CALayer/UIView)entire()
设置整段富文本优先级低于指定属性, 较为重要的属性 maxSize 设置绘制约束, 部分段落属性只在整段中设置生效drawLayer(^(CALayer*drawLayer)completion)
绘制layer, 无法响应手势drawView(^(UIView*drawView)completion)
绘制View, 可响应手势属性
通用属性
verticalOffset垂直偏移
onClicked点击回调
onLayout展示回调
cacheFrame缓存该段文本绘制位置
字符串属性
font字体:文字字体/图片居中对齐字体
color颜色
letterSpace字间距
strokeWidth描边宽度,整数为镂空,Color不生效;负数Color生效
strokeColor描边颜色
verticalForm文字绘制随文字书写方向,默认否(0),是(非0)
underline下划线类型,整形,0为none,1为细线2为加粗9为双条参考CTUnderlineStyle(仅枚举了三种,其他值也有不同效果)
图片属性
imageSize图片尺寸,默认为图片本身尺寸,会根据图片缩放(2x3x)自动调整
imageAlign图片对齐模式,0为默认,基准线对齐.1为居中对齐至特定字体大小参看ZJTextImageAlign
段落属性
maxSize绘制的约束尺寸,默认不限制
minLineSpace最小行间距
maxLineSpace最大行间距
minLineHeight最小行高
maxLineHeight最小行高
align对齐,整形,0为默认靠左1为靠右2为居中,参考CTTextAlignment
lineBreakMode对齐,整形,参考NSLineBreakMode
性能总体采用CoreText+异步绘制图片完成,理论上性能会比较高,经过测试如下数据供参考:
内容:一段文本加上两张图片
机型:iPhone6
测试结果:
常规(使用NSAttributedString+UILabel)过程:创建->显示(绘制)常规分析:
主线程代码在28ms左右.(主线程代码开始至结束耗时)
UILabel显示(绘制)耗时在42ms左右.(addSubview至drawRect耗时)
综合耗时70ms左右,全部在主线程
异步绘制(本框架)过程:创建->异步绘制->显示异步绘制分析:
主线程(创建)代码在28ms左右.(主线程代码开始至结束耗时)
创建(主线程)+异步绘制耗时84ms左右.(主线程代码开始至绘制出图片回调)
由1、2得出子线程绘制耗时56ms左右,另外经过多次试验(大段文字绘制)得出绘制复杂的段落也耗时增长较少
显示耗时0.75ms左右.(addSubview至drawRect耗时)
综合耗时85ms左右,其中主线程29ms,子线程56ms
结论:
相较于常规方式降低了主线程压力70ms->29ms
越复杂的文本收益越高(多控件合一,异步绘制),上图中大段富文本绘制时间也只多了15ms,耗时增长少
总体耗时增加了15ms,都在子线程,毕竟处理的逻辑比系统的多.
安装GithubZJAttributedText
Podpod 'ZJAttributedText'本框架依赖SDWebImage(几乎所有App都集成了,可以共用一套缓存逻辑)
尾巴内部实现代码不多,几乎所有步骤都添加了注释,如果需要学习CoreText,异步绘制,链式语法,还算是个不错的Demo,如果大家感兴趣,可以补充下CoreText相关内容,这部分网上的资料都比较老,错误也比较多.欢迎issue与star~
评论