美文网首页iOS面试
iOS设置圆角之:离屏渲染的问题和解决

iOS设置圆角之:离屏渲染的问题和解决

作者: 那抹浮沉 | 来源:发表于2020-07-08 16:01 被阅读0次
    • iOS中实现UIView及其子类的圆角效果有2中方法
    • 检测开启离屏渲染
    • 实现圆角两部曲
    • 设置圆角就一定会触发离屏渲染吗
      ** 不会触发离屏渲染的情况1
      **不会触发离屏渲染的情况2
      ** 会触发离屏渲染的情况1
      ** 会触发离屏渲染的情况2
    • 绘制图层的流程
    • 绘制圆角时出现离屏渲染的解决方案

    iOS中实现UIView及其子类的圆角效果有2中方法

    • 当前屏幕渲染,GPU直接在当前显示的屏幕的缓冲区进行图形渲染,不需要提前另开缓冲区也就不需要缓冲区的切换,因此性能更高
    • 离屏渲染,简单的来说就是提前另开一个缓冲区进行图形渲染,由于显示需要和当前屏幕缓冲区进行切换,所以很耗费性能,通常圆角,遮罩,不透明度,阴影,渐变,光栅化和抗锯齿等这些都会触发离屏渲染

    检测开启离屏渲染

    开启检测离屏渲染.jpg

    如图,如果触发了离屏渲染,会有浅黄色背景出现

    实现圆角两部曲

     im.layer.cornerRadius = 5;//设置圆角
    im.layer.masksToBounds = YES;//裁减
    

    没错,就是这么简单,那么你知道为什么要设置2个属性吗,既然是搭配使用,又是万年重复的代码,一个属性不好吗,那么请看

    圆角官宣.jpg
    是的,旁边的汉字说明一件非常清楚,因为他俩各司其职。
    那么问题又来了,少设置一个行不行,首先第一步是不能省的,第二步呢,请看下方 不会触发离屏渲染的情况1

    设置圆角就一定会触发离屏渲染吗

    绝大部分未深入研究的人肯定觉得设置圆角就一定会触发离屏渲染,事实并非如此,来看一张图

    触发渲染.jpg

    可以看出,触发离屏渲染需要3个条件

    • 1.contents 即:图片(设置图片即意味着添加了内容contents)
    • 2.背景色 或 border ,为什么说是而不是,因为他俩是2个图层,超过一个图册的渲染就会触发离屏渲染
    • 不会触发离屏渲染的情况1
        //不添加内容只设置背景色
        UIImageView *im = [[UIImageView alloc]initWithFrame:CGRectMake(10, 10, 100, 100)];
    
        //设置了背景颜色,仅有一个图层
        im.backgroundColor = [UIColor redColor];
        
        //设置圆角属性
        im.layer.cornerRadius = 50;//圆角
        //既然视图只有一个图层,还需要裁减吗,答案是不需要,
        im.layer.masksToBounds = YES;//裁减-无影响
    
        [self.view addSubview:im];
    
    • 不会触发离屏渲染的情况2
    //设置了图片,不设置背景色和border 无触发,如下,是不会触发离屏渲染
        UIImageView *im = [[UIImageView alloc]initWithFrame:CGRectMake(10, 10, 100, 100)];
    
        //设置了背景颜色和图片
        UIImage *ima = [UIImage imageNamed:@"girl"];
        
        //设置圆角属性
        im.layer.cornerRadius = 50;//圆角
        im.layer.masksToBounds = YES;//裁减
    
        [im setImage:ima];
        [self.view addSubview:im];
    
    • 会触发离屏渲染的情况1
    //设置了背景颜色和图片以及border属性
    //注意:border和背景色存在其一即可触发离屏渲染
        UIImageView *im = [[UIImageView alloc]initWithFrame:CGRectMake(10, 10, 100, 100)];
    
        //设置了背景颜色和图片
        im.backgroundColor = [UIColor redColor];
        UIImage *ima = [UIImage imageNamed:@"girl"];
        
        //设置圆角属性
        im.layer.cornerRadius = 50;//圆角
        im.layer.masksToBounds = YES;//裁减
        
        // 设置border宽度和颜色
        im.layer.borderWidth = 2.0;
        im.layer.borderColor = UIColor.blackColor.CGColor;
    
        [im setImage:ima];
        [self.view addSubview:im];
    
    • 会触发离屏渲染的情况2
        //创建UIImageView
        UIImageView *im = [[UIImageView alloc]initWithFrame:CGRectMake(10, 10, 100, 100)];
    
        im.layer.cornerRadius = 50;//
        im.layer.masksToBounds = YES;//将边界以外区域遮盖
    
        [self.view addSubview:im];
      //创建子view
        UIView *view2 = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100.0, 100.0)];
            // 下面3个任何一个属性
            // 设置背景色
    //        view2.backgroundColor = UIColor.blueColor;
    //        // 设置内容
    //        view2.layer.contents = (__bridge id)([UIImage imageNamed:@"girl"].CGImage);
    //        // 设置边框
    //        view2.layer.borderWidth = 2.0;
    //       view2.layer.borderColor = UIColor.blackColor.CGColor;
            [im addSubview:view2];
    
    

    绘制图层的流程

    看了上面3个例子,不知道大家有何感想,那么接下来提一下绘制图层的流程,不得不提到一个关键词 :油画算法
    油画算法(画家算法):先绘制场景中的离观察者较远的物体,再绘制较近的物体。

    触发渲染.jpg

    在看这个图片,绘制流程是这样的(3个图层)
    1.橘黄色背景,就像一个view,只加了背景色
    2.中间的图片,就是添加的内容,即contents
    3.最前面的border,呃,就是border

    那么在内存中他们是怎么操作的呢
    1.绘制背景,存放在离屏缓冲区
    2.绘制出contents,存放在离屏缓冲区
    3.绘制border,存放在离屏缓冲区
    4.3个图层依照画家算法显示在屏幕上,并清除离屏缓冲区
    5.进行圆角操作和裁减

    延伸非离屏渲染的情况
    1.绘制背景,存放在帧缓冲区
    2.显示在屏幕上,然后清除帧缓冲区
    3.绘制出contents,存放在帧缓冲区
    4.显示在屏幕上,然后清除帧缓冲区
    5.绘制border,存放在离屏缓冲区
    6.显示在屏幕上,然后清除帧缓冲区
    7.而此时,想要修改圆角,之前的图层已经丢弃,那么不就emmmm了

    扩展
    帧缓存区渲染流程:APP -> 帧缓存区 ->display
    离屏渲染流程:APP -> 离屏缓存区 ->display

    绘制圆角时出现离屏渲染的解决方案

    当前屏幕渲染实现圆角(直接在当前屏幕渲染绘制,提高性能)
    为UIImage类扩展一个实例方法

    //当前屏幕渲染, 扩展UIimage
    -(UIImage *)imageWithCornerRadius:(CGFloat)radius ofsize:(CGSize)size
    {
        //边界问题
        if(radius < 0){
            radius = 0;
        }else if (radius > MIN(size.height, size.width)){
            //如果radius大于最小边,取最小边的一般
            radius = MIN(size.height, size.width)/2;
        }
        
        //当前image的可见绘制区域
        CGRect rect = (CGRect){0, 0, size};
        //创建基于位图的上下文
        UIGraphicsBeginImageContextWithOptions(size, NO, UIScreen.mainScreen.scale);//scale:范围
        /*
         //在当前位图的上下文添加圆角绘制路径
         CGContextAddPath(UIGraphicsGetCurrentContext(), [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:radius].CGPath);
         //当前绘制路径和原绘制路径相交得到最终裁减绘制路径
         CGContextClip(UIGraphicsGetCurrentContext());
         */
        //等效于上面的2句代码
        [[UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:radius]addClip];
        //绘制
        [self drawInRect:rect];
        //取得裁减后的image
        UIImage *image =UIGraphicsGetImageFromCurrentImageContext();
        //关闭当前位图上下文
        UIGraphicsEndImageContext();
        return image;
        
    }
    

    需要的地方调用

        UIImageView *im = [[UIImageView alloc]initWithFrame:CGRectMake(10, 10, 100, 100)];
        UIImage *ima = [UIImage imageNamed:@"0"];
        ima = [ima imageWithCornerRadius:50 ofsize:im.frame.size];
        [im setImage:ima];
        [self.view addSubview:im];
    

    相关文章

      网友评论

        本文标题:iOS设置圆角之:离屏渲染的问题和解决

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