iOS - YYKit 之 YYText
本文只是让自己加深下理解,方便以后查看,原作者看到有不合适的地方,或者大神们看到有不对的地方希望指正 ~
原文地址,尊重原作者 ~
<br />
一. 特点和用法
关于YYText的特点和用法请看@ibireme大神的github
二. 使用到的组件
-
介绍YYLabel之前先说一下
YYTextAttribute
,因为后面会大量的使用到它。
YYTextAttribute -
定义的一些
Enum
,
YYTextAttributeType:attribute
的类型,有None
、UIKit
、CoreText
和YYText
四种类型;
YYTextLineStyle:line
的样式;
YYTextVerticalAlignment: 垂直方向text的位置;
YYTextDirection:text
的位置;
YYTextTruncationType:text
截断的位置。 -
YYText中定义的
Attribute Name
。主要是独有的一些类型。 -
YYTextBackedString:可以将一些表情图片映射成纯文本。
-
YYTextBinding:使一些特定的字符串绑定在一起,
YYTextView
在选择和编辑他们的时候把他们当成一个单独的字符。 -
YYTextShadow:用处和
NSShadow
一样,只是比NSShadow
多了一些功能,比如说可以使用blendMode
(图形混合模式)、可以在shadow上再加一层shadow。关于blendMode
的学习,可以参见喵神的博客 -
YYTextDecoration:实现下划线(underline)和中间截线(strikethrough)时使用,线条的形式给出了几种样式,可以通过YYTextLineStyle枚举查看。具体是
underline
还是strikethrough
是在NSAttributedString+YYText中NSMutableAttributeString(YYText)
中实现的方法。 -
YYTextBorder:实现在文本周围画一个border,也可以是填充一个背景色。
-
YYTextAttachment:封装需要放入
text
中的对象。在说明文档中提到,如果attachment
是UIImage
,就绘制到CGContext
,如果是UIView或者CALayer就加入到text container
的view或者layer中。 -
YYTextHightlight:当
YYLabel
或者YYTextView
中的text可以被用户按下时,被按下的text
会有一个highlighted
状态,这时候就需要是用YYTextHighlight
来修改原来的text
。所以这个对象和YYText
一样,只是是在highlight
状态下的YYText
,而且添加了点击和长按事件。 -
再来说一下NSAttributedString+YYText文件
在这个分类中主要是实现了几类的操作: -
一些操作当前
attributed string
的方法
比如说归档和反归档当前字符串、获得某个位置的attributes
、字间距、色值、背景色、shadow等等。具体的参见文件,基本上是作者封装的方便获得各种数据的方法。(和Foundation比强大太多了)。 -
为YYText创建
attachment
的方法 -
为YYText添加YYText特有的
attribute
的方法 -
添加像设置属性一样的设置
character attribute的font
、color
、backgroundColor
等等的方法 -
添加像设置属性一样的设置
paragraph attribute
的方法。 -
添加像设置属性一样的设置YYText
attribute
的方法。 -
使用
range
设置不连续的attribute的方法 -
设置
text highlight
的便捷方法 -
和其他的工具型的方法。
-
NSParagraphStyle+YYText文件
提供了CoreText
中的CTParagraphStyleRef
和NSParagraphStyle
之间的转化 -
YYTextParser
这是一个protocol
,声明了一个-(BOOL)parseText:(NSMutableAttributedString *)string selectedRange:(NSRangePointer)selectedRange;
方法。这个方式是遵守这个协议必须实现的方法,当YYTextView
或者YYLabel
中的text
改变时被调用。返回YES
说明修改了这个text
。
作者简单的实现了MarkdownParser
和EmotionParser
,两个原理都差不多一样,在这里只对EmotionParser
做一下简单的介绍,希望能有所启发:
这段代码(上图)就是生成正则表达式_regex
和映射的字典_mapper
,第一层for是获得你要匹配的key
,第二层是如果有这些特殊的字符需要转译一下,然后将这些需要比配的key
用“|”连接起来。
这个(上图)就是修改text
之后会被调用的方法,在这个方法里对输入的text
进行匹配,如果匹配到之前_mapper
中需要替换的字符,就将这个字符串替换为需要替换的表情符。
替换成表情符之后就需要重新计算这个表情符所占的range
了,这个方法就是拿到替换之后的的range
。
5.YYTextLayout
先看一下文档中的说明,如下图:
是不是很眼熟?好像在哪见过?是的,就是NSLayoutManager和NSTextContainer。他们的作用都是相似的。
-
YYTextContainer
支持矩形(CGSize)
和图形(UIBezierPath)
来初始化YYTextContainer;
在这里重点说一下YYTextLinePositionModifier,它是 一个协议,定义了一个必须实现的方法,这个方法将会在layout
完成的时候被调用,三个参数分别是存放YYTextLine
的数组、完整的text
和layout container
。
YYTextLine:它是封装了CTLineRef
的对象,封装了每一行text
的具体展示位置、range
、这一行拥有的attachments
等等,只有一个类方法的初始化方法。如果不了解一些自行描述集的内容,对textLine
中的一些属性和操作会不是很清晰,看下图:
边框(Bounding Box
):一个假想的边框,尽可能地容纳整个字形。
基线(Baseline
):一条假想的参照线,以此为基础进行字形的渲染。一般来说是一条横线。
基础原点(Origin
):基线上最左侧的点。
行间距(Leading
):行与行之间的间距。
字间距(Kerning
):字与字之间的距离,为了排版的美观,并不是所有的字形之间的距离都是一致的,但是这个基本步影响到我们的文字排版。
上行高度(Ascent
)和下行高度(Decent
):一个字形最高点和最低点到基线的距离,所以行高就是ascent + decent
。
看完上面的简单介绍你就能明白,在YYTextLine的setCTLine
中的代码逻辑是从CTLineRef
中取出对应的行宽、上行高度、下行高度、行间距、rangge
和第一个字型符的位置(这个在垂直布局会用到)。之后调用reloadBounds
方法,重新计算当前行的bounds
、attachments
所在的range
和rect
。 -
YYTextLayout
这个真的是核心内容了,这个文件一共3300多行的代码,从代码量上就能看出它的地位。这个类中存储着text
的layout
结果,所有的property
都是readonly
的。实现的接口有:
1、通过一些类方法初始化的方法(YYTextContainer
、CGSize
和text
)
2、layout
之后的attributes
,都是只读的
3、从layout
中读取信息(位置、range
等等)
4、绘制text layout
这个类主要是使用上面讲过的所有的数据来绘制text
,这部分的代码还是需要一点一点的去读的,如果是新手每一行都会有收获(比如说我),如果是老司机就没有必要一行行的读了,了解他的解题思路和解决这个问题的办法就可以。下面说一下生成layout
的那个500行代码的情况,就按照代码的顺序从上往下大概的说明一下干了什么。
* 1)、初始化一系列使用到的变量
2)、安全判断,text
和container
3)、判断是否需要修复emoji
的bug(iOS8.3中)
4)、判断是否设置了path
属性和exclusionPaths
数组,做相应的计算拿到CGPath
,如果CGPath
为空则goto fail
返回nil
(如果设置了path
,size
和insets
就没有用了)
5)、判断是不是奇偶填充,判断pathWidth
是否为0,判断是否是垂直展示
6)、使用text创建CTFramesetterRef
,创建失败goto fail
7)、使用上一步中创建的frameSetter
创建CTFrameRef
8)、从CTFrameRef
的对象中获得每一行、ctRun
数组,计算每一行的frame
,判断是否实现了linePositionModifier
这个协议,有的话调用协议方法。
9)、计算bounding size
10)、判断是否需要truncation
,和按那种方式处理
11)、判断是否垂直布局text
,需要的话,旋转布局
12)、判断得到的visibleRange
长度,有效的话遍历text
中的attributes
,配置对应的layout
属性
13)、配置layout中
的attachments
14)、配置结束,返回layout
绘制时就是根据layout中的配置情况绘制相应的特征,这段代码在此就不做分析了。
6.YYAsyncLayer文件
YYAsyncLayerDispalyTask
是在YYAsyncLayer去background queue
渲染是调用的对象,它有三个回调,一个willDisplay
在渲染之前、一个didDisplay
在渲染之后和渲染时被调用的display
。
YYAsyncLayer是CAlayer的子类,当这个layer
更新contents
时就会调用delegate
方法去调用async display task
去background queue
渲染。这个delegate
方法是YYAsyncLayerDelegate
的方法。
YYAsyncLayer在刷新时调用_displayAsync
:方法,然后调用遵守YYAsyncLayerDelegate
的对象实现的newAsyncDisplayTask
方法,获取到需要绘制的前后和绘制时的task
,根据是够需要异步来判断直接在主线程执行绘制代码还是异步执行绘制代码。
在异步绘制过程中用到了一个异步队列,获取方法是YYAsyncLayerGetDisplayQueue
,在这个方法中有一个关于QOS的概念,NSQualityOfService(QOS)
ios8之后提供的新功能,这个枚举值是要告诉操作系统我们在进行什么样的工作,让系统能通过合理的资源控制来最高效的执行任务代码,主要涉及CPU调度、IO优先级、任务运行在哪个线程以及运行的顺序等等。
枚举值的含义如下:
NSQualityOfServiceUserInteractive 与用户交互的任务,这些任务通常跟UI级别的刷新相关,比如动画,这些任务需要在一瞬间完成
NSQualityOfServiceUserInitiated 由用户发起的并且需要立即得到结果的任务,比如滑动scroll view时去加载数据用于后续cell的显示,这些任务通常跟后续的用户交互相关,在几秒或者更短的时间内完成
NSQualityOfServiceUtility 一些可能需要花点时间的任务,这些任务不需要马上返回结果,比如下载的任务,这些任务可能花费几秒或者几分钟的时间
NSQualityOfServiceBackground 这些任务对用户不可见,比如后台进行备份的操作,这些任务可能需要较长的时间,几分钟甚至几个小时
NSQualityOfServiceDefault 优先级介于user-initiated 和 utility,当没有 QoS信息时默认使用,开发者不应该使用这个值来设置自己的任务
Qos和GCD queue的对照图:
收获到的小知识点:
- iOS7 and later,
UIFont
和CTFontRef
是toll-free bridged
的,在iOS6,UIFont是对CTFontRref
的封装,所以在CoreText
中是可以使用UIFont的,但是在UILabel
和UITextView
中不能使用CTFontRe
f。 -
NSParagraphStype
不是toll-free bridged 到CTParagraphStypeRef
,CoreText
可以同时使用两者,但UILabel
和UITextView
只能使用NSParagraphStyle
。 - 查看
.a
静态文件支持哪种iOS处理器