小心别让圆角成了你列表的帧数杀手

来源:互联网 发布:资源数据库建设 编辑:程序博客网 时间:2024/05/02 01:52

//------1            imageView.layer.cornerRadius = 25;            imageView.clipsToBounds = YES;                        //------2            CAShapeLayer *shapeLayer = [CAShapeLayer layer];            UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:imageView.bounds];            shapeLayer.path = path.CGPath;            imageView.layer.mask = shapeLayer;                        //-------3//            [self test:imageView];            //-------4            imageView.layer.shouldRasterize = YES;            imageView.layer.rasterizationScale = [UIScreen mainScreen].scale;

- (void)test:(UIImageView *)imageView {    // Get your image somehow    UIImage *image = [UIImage imageNamed:@"12.png"];        // Begin a new image that will be the new image with the rounded corners    // (here with the size of an UIImageView)    UIGraphicsBeginImageContextWithOptions(imageView.bounds.size, NO, [UIScreen mainScreen].scale);        // Add a clip before drawing anything, in the shape of an rounded rect    [[UIBezierPath bezierPathWithRoundedRect:imageView.bounds                                cornerRadius:10] addClip];    // Draw your image    [image drawInRect:imageView.bounds];        // Get the image, here setting the UIImageView image    imageView.image = UIGraphicsGetImageFromCurrentImageContext();        // Lets forget about that we were drawing    UIGraphicsEndImageContext();}


下面说的基本正确,但是我亲测了

imageView.layer.shouldRasterize =YES;

imageView.layer.rasterizationScale = [UIScreenmainScreen].scale;

这个不仅不会变快,还会变慢!!!!!!!



  • Core Animation工具也提供了一系列复选框选项来帮助调试渲染瓶颈:
  • Color Blended Layers - 这个选项基于渲染程度对屏幕中的混合区域进行绿到红的高亮(也就是多个半透明图层的叠加)。由于重绘的原因,混合对GPU性能会有影响,同时也是滑动或者动画帧率下降的罪魁祸首之一。
  • ColorHitsGreenandMissesRed - 当使用shouldRasterizep属性的时候,耗时的图层绘制会被缓存,然后当做一个简单的扁平图片呈现。当缓存再生的时候这个选项就用红色对栅格化图层进行了高亮。如果缓存频繁再生的话,就意味着栅格化可能会有负面的性能影响了(更多关于使用shouldRasterize的细节见第15章“图层性能”)。
  • Color Copied Images - 有时候寄宿图片的生成意味着Core Animation被强制生成一些图片,然后发送到渲染服务器,而不是简单的指向原始指针。这个选项把这些图片渲染成蓝色。复制图片对内存和CPU使用来说都是一项非常昂贵的操作,所以应该尽可能的避免。
  • Color Immediately - 通常Core Animation Instruments以每毫秒10次的频率更新图层调试颜色。对某些效果来说,这显然太慢了。这个选项就可以用来设置每帧都更新(可能会影响到渲染性能,而且会导致帧率测量不准,所以不要一直都设置它)。
  • Color Misaligned Images - 这里会高亮那些被缩放或者拉伸以及没有正确对齐到像素边界的图片(也就是非整型坐标)。这些中的大多数通常都会导致图片的不正常缩放,如果把一张大图当缩略图显示,或者不正确地模糊图像,那么这个选项将会帮你识别出问题所在。
  • Color Offscreen-Rendered Yellow - 这里会把那些需要离屏渲染的图层高亮成黄色。这些图层很可能需要用shadowPath或者shouldRasterize来优化。
  • Color OpenGL Fast Path Blue - 这个选项会对任何直接使用OpenGL绘制的图层进行高亮。如果仅仅使用UIKit或者Core Animation的API,那么不会有任何效果。如果使用GLKView或者CAEAGLLayer,那如果不显示蓝色块的话就意味着你正在强制CPU渲染额外的纹理,而不是绘制到屏幕。
  • Flash Updated Regions - 这个选项会对重绘的内容高亮成黄色(也就是任何在软件层面使用Core Graphics绘制的图层)。这种绘图的速度很慢。如果频繁发生这种情况的话,这意味着有一个隐藏的bug或者说通过增加缓存或者使用替代方案会有提升性能的空间。


转载:-------------------------------------------------------------

前言

在iOS的世界,圆角无处不在,而且必须存在。因为圆角是符合人类视觉安全体验的,圆角让人觉得舒适,而方角在潜意识层次是具有伤害体验的,因为尖尖的东西总是有可能对人造成伤害的,所以我们更喜欢圆角。在我之前的文章中讲过,在iOS的中设置圆角是非常容易的一件事情,这也体现出苹果也是非常重视圆角这件事情的。

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

实测

layer.cornerRadius

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

1

2

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

aImageView.layer.masksToBounds = YES;

运行截图如下:


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


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


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


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


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


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

layer.mask

之前有的文章说通过layer.cornerRadius和layer.mask设置圆角并没有什么差异,事实真的是这样的吗?我如下设置了圆角:

1

2

3

4

CAShapeLayer *layer = [CAShapeLayer layer];  

UIBezierPath *aPath = [UIBezierPath bezierPathWithOvalInRect:aImageView.bounds];  

layer.path = aPath.CGPath;  

aImageView.layer.mask = layer;

得到的帧率如下:


竟然只有20帧每秒了,比layer.cornerRadius还少了8帧!!!所以layer.cornerRadius实现圆角的性能是要比layer.mask要高很多。

maskView

iOS的UIView多了一个maskView方法,不过这个东西和layer.mask是一个卵样的。

原理

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

Off-Screen Rendering

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

上下文切换

上下文切换,不管是在GPU渲染过程中,还是一直所熟悉的进程切换,上下文切换在哪里都是一个相当耗时的操作。首先我要保存当前屏幕渲染环境,然后切换到一个新的绘制环境,申请绘制资源,初始化环境,然后开始一个绘制,绘制完毕后销毁这个绘制环境,如需要切换到On-Screen Rendering或者再开始一个新的离屏渲染重复之前的操作。 下图描述了一次mask的渲染操作。


一次mask发生了两次离屏渲染和一次主屏渲染。即使忽略昂贵的上下文切换,一次mask需要渲染三次才能在屏幕上显示,这已经是普通视图显示3陪耗时,若再加上下文环境切换,一次mask就是普通渲染的30倍以上耗时操作。问我这个30倍以上这个数据怎么的出来的?当我在cell的UIImageView的实例增加到150个,并去掉圆角的时候,帧数才跌至28帧每秒。虽然不是甚准确,但至少反映mask这个耗时是无mask操作的耗时的数十倍的。

应对

那么如何应对这个问题呢?不要在滚动视图使用cornerRadius或者mask。如果你非要作死怎么办呢?那么这样也可以拯救你:

1

2

self.layer.shouldRasterize = YES;  

self.layer.rasterizationScale = [UIScreen mainScreen].scale;

这样大部分情况下可以马上挽救你的帧数在55帧每秒以上。shouldRasterize = YES会使视图渲染内容被缓存起来,下次绘制的时候可以直接显示缓存,当然要在视图内容不改变的情况下。

除了上面非要作死的人外,大家还是采取预先生成圆角图片,并缓存起来这个方法才是比较好的手段。预处理圆角图片可以在后台处理,处理完毕后缓存起来,再在主线程显示,这就避免了不必要的离屏渲染了。

另外也有在图片上面覆盖一个镂空圆形图片的方法可以实现圆形头像效果,这个也是极为高效的方法。缺点就是对视图的背景有要求,单色背景效果就最为理想。

总结

实现圆角cornerRadius要比mask高效很多。

Rasterize在大部分情况下极大减少GPU工作。在有空间的情况下,大部分情况下缓存总能帮到你,不是吗?

后台预处理图片也能很简单帮上你很大的忙。


0 0
原创粉丝点击