zoukankan      html  css  js  c++  java
  • YYText源码解读-YYText同步/异步渲染流程(二)

    [文章转载自40K CLUB APP]

    一、涉及到的几个类

    • YYLabel:Label控件,继承UIView
    • YYTextLayout、YYTextContainer、YYTextLine:用于布局计算,它是YYLabel的属性,当我们为YYLabel设置属性时(比如text、textColor等),它会计算出布局信息和完成内容的绘制。
    • YYTextAsyncLayer:自定义的Layer组件

    二、流程

    1 将YYLabel的默认Layer替换为YYTextAsyncLayer。

    从上一篇文章中得知,我们只需重写+layerClass方法即可。

    1 + (Class)layerClass {
    2 return [YYTextAsyncLayer class];
    3 }

    2 重写YYTextAsyncLayer的display方法

    1 - (void)display {
    2 super.contents = super.contents;
    3 // _displaysAsynchronously属性表示是否需要异步绘制,默认是NO
    4 [self _displayAsync:_displaysAsynchronously];
    5 }

    从上一篇文章中得知,display方法用来设置contents属性,而我们可以通过为contents赋一个CGImage的值,将YYLabel的结果,显示出来。这就需要我们通过YYLabel的所有属性(比如text、textColor等)来绘制一个CGImage出来,绘制的动作就是通过下面的方法完成的,绘制可以同步进行(默认),也可以异步进行,提高性能。

    1 - (void)_displayAsync:(BOOL)async {
    2 // 暂时省略内容,后续后逐行分析
    3 }

    要想生存内容图片,我们需要知道布局信息。

    布局信息的计算是通过YYLabel的YYTextLayout属性完成的。我们知道Layer的delegate是它的View(即YYTextAsyncLayer的delegate是YYLabel),我们为YYLabel实现一个newAsyncDisplayTask方法,YYTextAsyncLayer通过delegate调用该方法会返回一个YYTextAsyncLayerDisplayTask对象。通过YYTextAsyncLayerDisplayTask对象的block属性,YYTextAsyncLayer将上写文(context)回调给YYLabel的YYTextLayout属性,这时YYTextLayout的布局信息已经计算好,又获得了上下文信息(context),就可以完成绘制了。

    3 YYTextLayout的布局计算方法和绘制方法

    (1)布局计算方法

    1 + (YYTextLayout *)layoutWithContainer:(YYTextContainer *)container text:(NSAttributedString *)text range:(NSRange)range;

    (2)绘制方法

    1 - (void)drawInContext:(nullable CGContextRef)context
    2 size:(CGSize)size
    3 point:(CGPoint)point
    4 view:(nullable UIView *)view
    5 layer:(nullable CALayer *)layer
    6 debug:(nullable YYTextDebugOption *)debug
    7 cancel:(nullable BOOL (^)(void))cancel;

    4 YYTextLayout绘制好图片,YYTextAsyncLayer通过上下文(context)就能拿到图片,并设置给contents属性,这样整个Label的显示就完成了。

    最后看一下YYTextAsyncLayer的生成图片的方法

      1 - (void)_displayAsync:(BOOL)async {
      2 __strong id<YYTextAsyncLayerDelegate> delegate = (id)self.delegate;
      3 YYTextAsyncLayerDisplayTask *task = [delegate newAsyncDisplayTask];
      4 if (!task.display) {
      5 if (task.willDisplay) task.willDisplay(self);
      6 self.contents = nil;
      7 if (task.didDisplay) task.didDisplay(self, YES);
      8 return;
      9 }
     10 
     11 if (async) {
     12 if (task.willDisplay) task.willDisplay(self);
     13 _YYTextSentinel *sentinel = _sentinel;
     14 int32_t value = sentinel.value;
     15 BOOL (^isCancelled)() = ^BOOL() {
     16 return value != sentinel.value;
     17 };
     18 CGSize size = self.bounds.size;
     19 BOOL opaque = self.opaque;
     20 CGFloat scale = self.contentsScale;
     21 CGColorRef backgroundColor = (opaque && self.backgroundColor) ? CGColorRetain(self.backgroundColor) : NULL;
     22 if (size.width < 1 || size.height < 1) {
     23 CGImageRef image = (__bridge_retained CGImageRef)(self.contents);
     24 self.contents = nil;
     25 if (image) {
     26 dispatch_async(YYTextAsyncLayerGetReleaseQueue(), ^{
     27 CFRelease(image);
     28 });
     29 }
     30 if (task.didDisplay) task.didDisplay(self, YES);
     31 CGColorRelease(backgroundColor);
     32 return;
     33 }
     34 
     35 dispatch_async(YYTextAsyncLayerGetDisplayQueue(), ^{
     36 if (isCancelled()) {
     37 CGColorRelease(backgroundColor);
     38 return;
     39 }
     40 UIGraphicsBeginImageContextWithOptions(size, opaque, scale);
     41 CGContextRef context = UIGraphicsGetCurrentContext();
     42 if (opaque) {
     43 CGContextSaveGState(context); {
     44 if (!backgroundColor || CGColorGetAlpha(backgroundColor) < 1) {
     45 CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor);
     46 CGContextAddRect(context, CGRectMake(0, 0, size.width * scale, size.height * scale));
     47 CGContextFillPath(context);
     48 }
     49 if (backgroundColor) {
     50 CGContextSetFillColorWithColor(context, backgroundColor);
     51 CGContextAddRect(context, CGRectMake(0, 0, size.width * scale, size.height * scale));
     52 CGContextFillPath(context);
     53 }
     54 } CGContextRestoreGState(context);
     55 CGColorRelease(backgroundColor);
     56 }
     57 task.display(context, size, isCancelled);
     58 if (isCancelled()) {
     59 UIGraphicsEndImageContext();
     60 dispatch_async(dispatch_get_main_queue(), ^{
     61 if (task.didDisplay) task.didDisplay(self, NO);
     62 });
     63 return;
     64 }
     65 UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
     66 UIGraphicsEndImageContext();
     67 if (isCancelled()) {
     68 dispatch_async(dispatch_get_main_queue(), ^{
     69 if (task.didDisplay) task.didDisplay(self, NO);
     70 });
     71 return;
     72 }
     73 dispatch_async(dispatch_get_main_queue(), ^{
     74 if (isCancelled()) {
     75 if (task.didDisplay) task.didDisplay(self, NO);
     76 } else {
     77 self.contents = (__bridge id)(image.CGImage);
     78 if (task.didDisplay) task.didDisplay(self, YES);
     79 }
     80 });
     81 });
     82 } else {
     83 [_sentinel increase];
     84 if (task.willDisplay) task.willDisplay(self);
     85 UIGraphicsBeginImageContextWithOptions(self.bounds.size, self.opaque, self.contentsScale);
     86 CGContextRef context = UIGraphicsGetCurrentContext();
     87 if (self.opaque) {
     88 CGSize size = self.bounds.size;
     89 size.width *= self.contentsScale;
     90 size.height *= self.contentsScale;
     91 CGContextSaveGState(context); {
     92 if (!self.backgroundColor || CGColorGetAlpha(self.backgroundColor) < 1) {
     93 CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor);
     94 CGContextAddRect(context, CGRectMake(0, 0, size.width, size.height));
     95 CGContextFillPath(context);
     96 }
     97 if (self.backgroundColor) {
     98 CGContextSetFillColorWithColor(context, self.backgroundColor);
     99 CGContextAddRect(context, CGRectMake(0, 0, size.width, size.height));
    100 CGContextFillPath(context);
    101 }
    102 } CGContextRestoreGState(context);
    103 }
    104 task.display(context, self.bounds.size, ^{return NO;});
    105 UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    106 UIGraphicsEndImageContext();
    107 self.contents = (__bridge id)(image.CGImage);
    108 if (task.didDisplay) task.didDisplay(self, YES);
    109 }
    110 }

    [文章转载自40K CLUB APP]

  • 相关阅读:
    交换实验
    路由引入和控制
    ISIS
    BGP联盟
    BGP2
    bgp
    Linux日常总结
    配置本地yum源方法
    达梦数据库常见问题-安装
    达梦数据库常见问题-安装
  • 原文地址:https://www.cnblogs.com/fkclub/p/12402085.html
Copyright © 2011-2022 走看看