美文网首页
四、离屏渲染

四、离屏渲染

作者: Mjs | 来源:发表于2020-10-28 15:46 被阅读0次

    离屏渲染与正常渲染

    屏幕上最终显示的数据有两种加载流程

    • 正常渲染加载流程
    • 离屏渲染加载流程


      离屏渲染与正常渲染

    常⻅触发离屏渲染的⼏种情况:

    1. 使⽤了 mask 的 layer (layer.mask)
    2. 需要进⾏裁剪的 layer (layer.masksToBounds / view.clipsToBounds)
    3. 设置了组透明度为 YES,并且透明度不为 1 的 layer (layer.allowsGroupOpacity/
      layer.opacity)
    4. 添加了投影的 layer (layer.shadow*)
    5. 采⽤了光栅化的 layer (layer.shouldRasterize)
    6. 绘制了⽂字的 layer (UILabel, CATextLayer, Core Text 等)
    针对光栅化的使用,有以下几个建议:
    • 如果layer不能被复用,则没有必要开启光栅化
    • 如果layer不是静态,需要被频繁修改(例如动画过程中),此时开启光栅化反而影响效率
    • 离屏渲染缓存内容有时间限制,如果100ms内没有被使用,那么就会丢弃,无法进行复用
    • 离屏渲染的缓存空间有限,是屏幕的2.5倍,超过2.5倍屏幕像素大小的话也会失效,无法实现复用
    圆角中离屏渲染的触发时机

    在讲圆角之前,首先说明下CALayer的构成,如图所示,它是由backgroundColor、contents、borderWidth&borderColor构成的。跟我们即将解释的圆角触发离屏渲染息息相关。


    CALayer结构.png

    设置圆角

    • 方案一
    view.layer.cornerRadius = 2
    view.layer.masksToBounds = true // 触发离屏渲染的原因
    

    官网是这样介绍 cornerRadius

    Setting the radius to a value greater than 0.0 causes the layer to begin drawing rounded corners on its background. By default, the corner radius does not apply to the image in the layer’s contents property; it applies only to the background color and border of the layer. However, setting the masksToBounds property to true causes the content to be clipped to the rounded corners.
    将半径设置为大于0.0的值将使图层开始在其背景上绘制圆角。默认情况下,在图层的内容属性中,角半径不适用于图像;它只适用于背景颜色和层的边界。但是,将masksToBounds属性设置为true会导致内容被剪切到圆角。

    官方文档告诉我们,设置cornerRadius只会对CALayer中的backgroundColor 和 boder设置圆角,不会设置contents的圆角,如果contents需要设置圆角,需要同时将maskToBounds / clipsToBounds设置为true。

    所以我们可以理解为圆角不生效的根本原因是没有对contents设置圆角,而按钮设置的image是放在contents里面的,所以看到的界面上的就是image没有进行圆角裁剪。

    • 方案二
    - (UIImage *)roundedCornerImagewithCornerRadius:(CGFloat)cornerRadius {
    CGFloat w= self.size.width;
    CGFloat h= self.size.height;
    CGFloat scale = [UIScreen mainScreen],scale;//防止圆角半径小于0,或者大于宽/高中较小值的一半。
    if(cornerRadius < 6)cornerRadius =0;
    else if(cornerRadius >MIN(w, h))
    cornerRadius=MIN(w,h)/2.;UIImage *image= nil;
    CGRect imageFrame = CGRectMake(0.,0.,w, h);
    UIGraphicsBeginImageContextwithOptions(self.size, NO, scale);
    [[UIBezierPath bezierPathwithRoundedRect:imageFrame cornerRadius:cornerRadius] adCLip]
    [self drawInRect:imageFrame];
    image = UIGraphicsGetImageFromCurrentImageContext();UIGraphicsEndImageContext();return image;
    }
    
    • 方案三 YYImage处理方案
    - (UIImage *)yy_imageByRoundCornerRadius:(CGFloat)radius 
     corners:(UIRectCorner)corners 
     borderWidth:(CGFloat)borderWidth 
     borderColor:(UIColor *)borderColor 
     borderLineJoin:(CGLineJoin)borderLineJoin { 
     if (corners != UIRectCornerAllCorners) { 
     UIRectCorner tmp = 0; 
     if (corners & UIRectCornerTopLeft) tmp |= UIRectCornerBottomLeft; 
     if (corners & UIRectCornerTopRight) tmp |= UIRectCornerBottomRight; 
     if (corners & UIRectCornerBottomLeft) tmp |= UIRectCornerTopLeft; 
     if (corners & UIRectCornerBottomRight) tmp |= UIRectCornerTopRight; 
     corners = tmp; 
     } 
     UIGraphicsBeginImageContextWithOptions(self.size, NO, self.scale); 
     CGContextRef context = UIGraphicsGetCurrentContext(); 
     CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height); 
     CGContextScaleCTM(context, 1, -1); 
     CGContextTranslateCTM(context, 0, -rect.size.height); 
     CGFloat minSize = MIN(self.size.width, self.size.height); 
     if (borderWidth < minSize / 2) { 
     UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectInset(rect, borderWidth, borderWidth) byRoundingCorners:corners 
    cornerRadii:CGSizeMake(radius, borderWidth)]; 
     [path closePath]; 
     CGContextSaveGState(context); 
     [path addClip]; 
     CGContextDrawImage(context, rect, self.CGImage); 
     CGContextRestoreGState(context); 
     } 
     if (borderColor && borderWidth < minSize / 2 && borderWidth > 0) { 
     CGFloat strokeInset = (floor(borderWidth * self.scale) + 0.5) / self.scale; 
     CGRect strokeRect = CGRectInset(rect, strokeInset, strokeInset); 
     CGFloat strokeRadius = radius > self.scale / 2 ? radius - self.scale / 2 : 0; 
     UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:strokeRect byRoundingCorners:corners cornerRadii:CGSizeMake(strokeRadius, 
    borderWidth)]; 
     [path closePath]; 
     path.lineWidth = borderWidth; 
     path.lineJoinStyle = borderLineJoin; 
     [borderColor setStroke];
    

    总结

    当只设置backgroundColor、border,而contents中没有子视图时,无论maskToBounds / clipsToBoundstrue还是false,都不会触发离屏渲染
    当contents中有子视图时,此时设置 cornerRadius+maskToBounds / clipsToBounds,就会触发离屏渲染,但是这种情况在UIImageView中并不适用,当UIImageView中只设置图片+maskToBounds / clipsToBounds是不会触发离屏渲染,苹果对UIImageView优化我想也只是将image直接画在了contents上面这样不设置背景色其实只需要渲染一个layer,所以不需要用到离屏缓冲区

    相关文章

      网友评论

          本文标题:四、离屏渲染

          本文链接:https://www.haomeiwen.com/subject/jtmqvktx.html