Graver文字绘制核心类
美团开源Graver框架:用“雕刻”诠释iOS端UI界面的高效渲染
在Graver初探中,探索了Graver
的绘制周期。本着一探究竟的心态,来探索Graver
具体的绘制过程和涉及的核心类。
Graver
中绘制的核心类主要为:WMGTextDrawer
,WMGTextLayout
,WMGTextLayoutFrame
,WMGTextLayoutLine
,WMGTextLayoutRun
.每个类的主要方法和作用我做了一个简单的汇总,如下图所示:
WMGTextLayoutRun
:根据文本组件,创建一个CTRunDelegateRef
对象,在CoreText的图文混排中,具体的实现是:在一个NSAttributeString
中插入一个空字符\uFFFC
作为占位符,由CTRunDelegateRef
确定该字符的width
和height
,预留出图片的位置。在NSAttributeString
绘制完成后,在将图片绘制到根据CTRunDelegateRef
预留出来的区域中。
WMGTextLayoutLine
:行信息,根据CTLine
获取该行的_lineWidth(行宽)
,是否截断
,中划线
等信息,CTLineGetGlyphRuns
方法通过CTLine
可以得到对应行的CTRun
信息。CTLineGetStringIndexForPosition
方法可以通过点击点获得点击点的字符在该行中的位置。
WMGTextLayoutFrame
:根据CTFrameRef
获得。1,通过CTFrameRef
确定layoutSize
。2,根据CTFrameRef
得到CTLine
和每一个CTLine
的基线原点。3,依据textLayout
的最大行数,确定是否需要截断,更换截断字符。4,将CTLine的信息保存到WMGTextLayoutLine
中。
WMGTextLayout
:文字的布局对象。主要包括NSAttributeString
、maximumNumberOfLines
、heightSensitiveLayout
, baselineFontMetrics
,当任意一个属性变化时,就会根据NSAttributeString
自动创建一个WMGTextLayoutFrame
。
WMGTextDrawer
:文字绘制对象。通过- (void)drawInContext:(CGContextRef)ctx visibleRect:(CGRect)visibleRect replaceAttachments:(BOOL)replaceAttachments shouldInterruptBlock:(WMGTextDrawerShouldInterruptBlock)block
方法进行绘制,WMGTextDrawer
通过frame
属性设置总的绘制区域,根据WMGTextLayout
中的WMGTextLayoutFrame
,然后根据WMGTextLayoutLine
的CTLine
进行逐行绘制(CTLineDraw)
。另外WMGTextDrawerDelegate
:负责对NSAttributeString
中的WMGAttachment
进行绘制。WMGTextDrawerEventDelegate
:负责处理图文混排中,图片,文字,超链接,电话的点击事件。WMGActiveRange
:响应点击事件的文字区域。
接下来我们直接通过WMGTextDrawer
进行绘制,新建ExampleView
,重写-(void)drawRect:(CGRect)rect
_attrStr = [[NSMutableAttributedString alloc] initWithString:@"这些喜欢矫"];
// 新建图片组件信息
WMGTextAttachment *attachment = [[WMGTextAttachment alloc] init];
attachment.type = WMGAttachmentTypeStaticImage;
attachment.contents = [UIImage imageNamed:@"aiqing"];
attachment.size = CGSizeMake(40, 40);
attachment.position = 5;// 在字符串中的index
attachment.length = 1;// 所占字符长度
attachment.userInfo = @{@"content": attachment.contents};
self.textAttachment = attachment;
// 设置占位符的高度
WMGFontMetrics fontMetric = WMGFontMetricsMake(attachment.size.height, 0, 0);
attachment.baselineFontMetrics = fontMetric;
// 给图片添加点击事件
[attachment addTarget:self action:@selector(clickAction:) forControlEvents:UIControlEventTouchUpInside];
NSAttributedString *str1 = [NSAttributedString wmg_attributedStringWithTextAttachment:attachment];
NSAttributedString *str2 = [[NSAttributedString alloc] initWithString:@"枉过正的中国近代知识分子,他们不否定读书人群体存在的必要性,但主张自己要和工人打成一片"];
[_attrStr appendAttributedString:str1];
[_attrStr appendAttributedString:str2];
[_attrStr addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:20] range:NSMakeRange(0, _attrStr.string.length)];
NSMutableAttributedString *strdd = [[NSMutableAttributedString alloc] initWithString:_attrStr.string];
// 拼接文本组件信息
[strdd addAttribute:WMGTextAttachmentAttributeName value:attachment range:NSMakeRange(5, 1)];
CTRunDelegateRef delegate = [WMGTextLayoutRun textLayoutRunWithAttachment:attachment];
[strdd addAttribute:(NSString * )kCTRunDelegateAttributeName value:(__bridge id )delegate range:NSMakeRange(5, 1)];
// 添加蓝色字体
[strdd addAttribute:(NSString *)kCTForegroundColorAttributeName value:[UIColor blueColor] range:NSMakeRange(8, 5)];
[strdd addAttribute:(NSString *)kCTFontAttributeName value:[UIFont systemFontOfSize:17] range:NSMakeRange(0, strdd.string.length)];
_textDrawer = [[WMGTextDrawer alloc] init];
// frame 必须设置
_textDrawer.frame = self.bounds;
// 设置最大行数
_textDrawer.textLayout.maximumNumberOfLines = 2;
_textDrawer.textLayout.attributedString = strdd;
// 设置点击事件代理
_textDrawer.eventDelegate = self;
看下效果:
Simulator Screen Shot - iPhone.png