zoukankan      html  css  js  c++  java
  • iOS图片设置圆角性能优化

    问题

    圆角虽好,但如果使用不当,它就是你的帧数杀手,特别当它出现在滚动列表的时候。下面来看圆角如何毁掉你的流畅度的。

    实测

    layer.cornerRadius

    我创建了一个简单地UITableView视图,为每个cell添加了2个UIImageView实例,且为UIImageView实例进行如下设置

    aImageView.layer.cornerRadius = aImageView.frame.size.width/2.0;

    aImageView.layer.masksToBounds = YES;

    运行截图如下:


     

    你们猜,现在滚动的帧率是多少。


     

    已经跌至45帧每秒,这个帧率已经让人感觉到不那么顺滑了,如果低于40帧每秒,普通用户就会察觉明显的不流畅了。当我把cell的UIImageView实例增加至四个


     

    现在帧率已经低于30帧每秒了


     

    这个帧率如果出现在首屏,足以引领你的app进入垃圾级别的体验了。 现在我把UIImageView实例的size调的小一些。


     

    平均帧率提高了大概3帧每秒。


     

    在这里视图和圆角的大小对帧率并没有什么卵影响,数量才是伤害的核心输出啊。

    原理

    上面拖慢帧率的原因其实都是Off-Screen Rendering(离屏渲染)的原因。离屏渲染是个好东西,但是频繁发生离屏渲染是非常耗时的。

    Off-Screen Rendering

    离屏渲染,指的是GPU在当前屏幕缓冲区以外新开辟一个缓冲区进行渲染操作。由上面的一个结论视图和圆角的大小对帧率并没有什么卵影响,数量才是伤害的核心输出啊。可以知道离屏渲染耗时是发生在离屏这个动作上面,而不是渲染。为什么离屏这么耗时?原因主要有创建缓冲区和上下文切换。创建新的缓冲区代价都不算大,付出最大代价的是上下文切换

    上下文切换

    上下文切换,不管是在GPU渲染过程中,还是一直所熟悉的进程切换,上下文切换在哪里都是一个相当耗时的操作。首先我要保存当前屏幕渲染环境,然后切换到一

    个新的绘制环境,申请绘制资源,初始化环境,然后开始一个绘制,绘制完毕后销毁这个绘制环境,如需要切换到On-Screen

     Rendering或者再开始一个新的离屏渲染重复之前的操作。 下图描述了一次mask的渲染操作。


     

    一次mask发生了两次离屏渲染和一次主屏渲染。即使忽略昂贵的上下文切换,一次mask需要渲染三次才能在屏幕上显示,这已经是普通视图显示3陪耗时,若

     再加上下文环境切换,一次mask就是普通渲染的30倍以上耗时操作。问我这个30倍以上这个数据怎么的出来的?当我在cell的UIImageView

     的实例增加到150个,并去掉圆角的时候,帧数才跌至28帧每秒。虽然不是甚准确,但至少反映mask这个耗时是无mask操作的耗时的数十倍的。

     

    第一种:设置CALayer的cornerRadius

     imageView.image = [UIImage imageNamed:@"img"];
     imageView.image.layer.cornerRadius = 5;
     imageView.image.layer.masksToBounds = YES;

    这样设置会触发离屏渲染,比较消耗性能。比如当一个页面上有十几头像这样设置了圆角

    会明显感觉到卡顿。
    这种就是最常用的,也是最耗性能的。

    注意:ios9.0之后对UIImageView的圆角设置做了优化,UIImageView这样设置圆角
    不会触发离屏渲染,ios9.0之前还是会触发离屏渲染。而UIButton还是都会触发离屏渲染。

    第二种

    imageView.clipsToBounds = YES;
    imageView.layer setCornerRadius:50];
    imageView.layer.shouldRasterize = YES;

    shouldRasterize=YES设置光栅化,可以使离屏渲染的结果缓存到内存中存为位图, 使用的时候直接使用缓存,节省了一直离屏渲染损耗的性能。

    但是如果layer及sublayers常常改变的话,它就会一直不停的渲染及删除缓存重新 创建缓存,所以这种情况下建议不要使用光栅化,这样也是比较损耗性能的。

    第三种 :通过Core Graphics重新绘制带圆角的视图

    这种方式性能最好,但是UIButton上不知道怎么绘制,可以用UIimageView添加个 点击手势当做UIButton使用

    @implementation UIImage (CircleImage)
    
    - (UIImage *)drawCircleImage {
       UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, [UIScreen mainScreen].scale); 
       [[UIBezierPath bezierPathWithRoundedRect:self.bounds cornerRadius:50] addClip]; 
       [self drawInRect:self.bounds]; 

    UIImage *output = UIGraphicsGetImageFromCurrentImageContext();
       UIGraphicsEndImageContext();
       return output; 
    }
    @end
    //在需要圆角时调用如下
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        UIImage *img = [[UIImage imageNamed:@"image.png"] drawCircleImage];
        dispatch_async(dispatch_get_main_queue(), ^{
            imageView.image = img;
        });
    });

    四、通过混合图层

    此方法就是在要添加圆角的视图上再叠加一个部分透明的视图,只对圆角部分进行遮挡。图层混合的透明度处理方式与mask正好相反。此方法虽然是最优解,没有离屏渲染,没有额外的CPU计算,但是应用范围有限。

    
    

    总结

    1. 在可以使用混合图层遮挡的场景下,优先使用第四种方法。
    2. 即使是非iOS9以上系统,第一种方法在综合性能上依然强于后两者,iOS9以上由于没有了离屏渲染更是首选。
    3. 方法三由于需要大量计算和增加部分内存,需要实际情况各自取舍。
     
  • 相关阅读:
    点击按钮倒计时
    js实现-小框框全选
    CSS文字,文本,背景,盒模型等记录
    xps9560黑苹果展示
    面试——谈谈你对抽象和接口的理解(小知识大考点)
    谈谈你对Java 面向对象思想的理解
    谈谈你对Java 平台的理解
    HashMap1.7 问题总结
    2.3.2 InnoDB内存
    2.3 InnoDB 体系架构
  • 原文地址:https://www.cnblogs.com/junhuawang/p/5652220.html
Copyright © 2011-2022 走看看