YYText 源码解析 —— YYLabel 之一

架构

68747470733a2f2f7261772e6769746875622e636f6d2f69626972656d652f5959546578742f6d61737465722f417474726962757465732f6172636869746563747572652e706e67
(引用作者框架对比图片)

YYText包括控件(YYLabel,YYTextView),布局(YYTextLayout,NSAttributedString,YYTextContainer),核心(CoreText)。

源码目录组织

YYLabel

继承关系

YYLabel 直接继承 UIView,作者自己实现 Label 的渲染。+_+

state

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
struct {
// 是否需要更新布局
unsigned int layoutNeedUpdate : 1;
unsigned int showingHighlight : 1;
unsigned int trackingTouch : 1;
unsigned int swallowTouch : 1;
unsigned int touchMoved : 1;
unsigned int hasTapAction : 1;
unsigned int hasLongPressAction : 1;
// 内容是否支持逐渐消失
unsigned int contentsNeedFade : 1;
} _state;

如何熏染出 Text

YYLabel 的熏染是通过 YYTextAsyncLayerDelegate 完成。 YYTextAsyncLayerDelegate 提供了 newAsyncDisplayTask 配置方法用来实现渲染。

YYTextAsyncLayer 与 YYLabel 的熏染流程

setText 为例,看看这个熏染的流程。

  • [self.layer setNeedsDisplay] 这里调用的是 YYTextAsyncLayersetNeedsDisplay 。因为 layerClass 方法被重写了,返回了 YYTextAsyncLayer 。所以主layer所使用的类是 YYTextAsyncLayer
  • _cancelAsyncDisplay 的实现,后续再探讨。。。记遗留问题。
  • 这里 display 也被重写了,在这了执行了渲染最为关键的函数 _displayAsync
  • YYTextAsyncLayer 提供了 displaysAsynchronously 用来控制是否进行异步渲染,默认为异步熏染。

熏染

本人对 Core Graphics Framework 部分不同熟悉,个人重点对这一块代码进行学。刚才我们发现这里的渲染分为:普通渲染(ps:不知道怎么称,叫同步渲染感觉不太适合,就叫普通渲染吧)和异步渲染。这两种渲染的渲染代码处理基本是一样的,只是异步熏染增加了一些多线程的处理的考虑(比如:多线程经典的“读与写”的问题)。

所以就直接学习普通渲染部分的代码吧#^_^#

YYTextAsyncLayer 熏染学习

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
UIGraphicsBeginImageContextWithOptions(self.bounds.size, self.opaque, self.contentsScale);
CGContextRef context = UIGraphicsGetCurrentContext();
if (self.opaque) {
CGSize size = self.bounds.size;
size.width *= self.contentsScale;
size.height *= self.contentsScale;
CGContextSaveGState(context); {
if (!self.backgroundColor || CGColorGetAlpha(self.backgroundColor) < 1) {
CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor);
CGContextAddRect(context, CGRectMake(0, 0, size.width, size.height));
CGContextFillPath(context);
}
if (self.backgroundColor) {
CGContextSetFillColorWithColor(context, self.backgroundColor);
CGContextAddRect(context, CGRectMake(0, 0, size.width, size.height));
CGContextFillPath(context);
}
} CGContextRestoreGState(context);
}
task.display(context, self.bounds.size, ^{return NO;});
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
self.contents = (__bridge id)(image.CGImage);
  • 开始是调用 UIGraphicsBeginImageContextWithOptions 创建一个图片处理的上下文。 绘制结束调用 UIGraphicsEndImageContext
  • 通过 UIGraphicsGetCurrentContext 获取当前图形的上下文。
  • 这里有个 opaque 属性表示是否不透明,详细属性参见
  • CGContextSaveGState 把上下文保存到栈中。然后通过 CGContextSetFillColorWithColor 设置填充颜色, CGContextAddRect 添加矩形,CGContextFillPath 填充上下文路径。 最后保存通过 CGContextRestoreGState 保存最近的上下文。
  • 调用 task.display 回调,这里是调用配置的渲染方法,后面再分析 YYLabel 的熏染回调。 YYLabel 的熏染主要通过 YYTextLayout 来处理。 这么后续再做学习。
  • 通过 UIGraphicsGetImageFromCurrentImageContext 当前的上下文绘制出来的图片,结束绘制。
  • 最后将图片赋值给 contents 属性完成熏染。