iOS图片设置圆角性能问题

640?wx_fmt=gif

640?wx_fmt=png

程序君个人微信 和我聊聊编程和创业的事 加好友


作者丨齐滇大圣
https://www.jianshu.com/p/34189f62bfd8


一般我们在iOS开发的过程中设置圆角都是如下这样设置的。


avatarImageView.clipsToBounds = YES;
 [avatarImageView.layer setCornerRadius:50];

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

 注意:png图片UIImageView处理圆角是不会产生离屏渲染的。(ios9.0之后不会离屏渲染,ios9.0之前还是会离屏渲染)。


所有如果要高性能的设置圆角就需要找另外的方法了。下面是我找到的一些方法并写了一个例子。


640?wx_fmt=other


640?wx_fmt=gif设置圆角的方法


直接使用setCornerRadius
这种就是最常用的,也是最耗性能的。


setCornerRadius设置圆角之后,shouldRasterize=YES光栅化


avatarImageView.clipsToBounds = YES;
[avatarImageView.layer setCornerRadius:50];
avatarImageView.layer.shouldRasterize = YES;
avatarImageViewUrl.layer.rasterizationScale=[UIScreen mainScreen].scale;  //UIImageView不加这句会产生一点模糊

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

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


直接覆盖一张中间为圆形透明的图片(推荐使用)


这种方法就是多加了一张透明的图片,GPU计算多层的混合渲染blending也是会消耗一点性能的,但比第一种方法还是好上很多的。


UIImage drawInRect绘制圆角


这种方式GPU损耗低内存占用大,而且UIButton上不知道怎么绘制,可以用


UIimageView添加个点击手势当做UIButton使用。


UIGraphicsBeginImageContextWithOptions(avatarImageView.bounds.size, NO, [UIScreen mainScreen].scale);
[[UIBezierPath bezierPathWithRoundedRect:avatarImageView.bounds
                              cornerRadius:50] addClip];
[image drawInRect:avatarImageView.bounds];
avatarImageView.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

这段方法可以写在SDWebImage的completed回调里,在主线程异步绘制。
也可以封装到UIImageView里,写了个DSRoundImageView。后台线程异步绘制,不会阻塞主线程。


640?wx_fmt=gif问题:这种方法图片很多的话CUP消耗会高,内存占用也会暴增,而且后台线程绘制会比在主线程绘制占用更多的内存,不知道怎么解决?求大神指教!


SDWebImage处理图片时Core Graphics绘制圆角

//UIImage绘制为圆角
  int w = imageSize.width;
  int h = imageSize.height;
  int radius = imageSize.width/2;

  UIImage *img = image;
  CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
  CGContextRef context = CGBitmapContextCreate(NULL, w, h, 84 * w, colorSpace, kCGImageAlphaPremultipliedFirst);
  CGRect rect = CGRectMake(00, w, h);

  CGContextBeginPath(context);
  addRoundedRectToPath(context, rect, radius, radius);
  CGContextClosePath(context);
  CGContextClip(context);
  CGContextDrawImage(context, CGRectMake(00, w, h), img.CGImage);
  CGImageRef imageMasked = CGBitmapContextCreateImage(context);
  img = [UIImage imageWithCGImage:imageMasked];

  CGContextRelease(context);
  CGColorSpaceRelease(colorSpace);
  CGImageRelease(imageMasked);


以上代码我写成了UIImage的类别:UIImage+DSRoundImage.h
并在SDWebImage库里处理image的时候使用类别方法绘制圆角并缓存。


640?wx_fmt=gif使用Instruments的Core Animation查看性能


Color Offscreen-Rendered Yellow


开启后会把那些需要离屏渲染的图层高亮成黄色,这就意味着黄色图层可能存在性能问题。


Color Hits Green and Misses Red


如果shouldRasterize被设置成YES,对应的渲染结果会被缓存,如果图层是绿色,就表示这些缓存被复用;如果是红色就表示缓存会被重复创建,这就表示该处存在性能问题了。


用Instruments测试得


第一种方法,UIImageView和UIButton都高亮为黄色。


第二种方法,UIImageView和UIButton都高亮为绿色


第三种方法,无任何高亮,说明没离屏渲染。


这种圆片覆盖的方法一般只用在底色为纯色的时候,如果圆角图片的父View是张图片的时候就没办法了,而且底色如果是多种颜色的话那要做多张不同颜色的圆片覆盖。(可以用代码取底色的颜色值给圆片着色)


第四种方法无任何高亮,说明没离屏渲染(但是CPU消耗和内存占用会很大)


第五种方法无任何高亮,说明没离屏渲染,而且内存占用也不大。(暂时感觉是最优方法)


640?wx_fmt=gif问题回复:


有回复提到还有一种mask方法。


这种方法比第一种方法其实更卡顿。一次mask发生了两次离屏渲染和一次主屏渲染。 具体可以参考小心别让圆角成了你列表的帧数杀手

http://www.cocoachina.com/ios/20150803/12873.html


@nerozhao说第四种比第一种更卡。


我刚在demo里加了个例子测试了一下,第一种能明显的感觉到卡顿,第四种还是挺顺畅
的,有兴趣的可以自己试试看。第四种是解决了离屏渲染GPU的问题。


可以用Instruments的 GPU Driver进行测试:

Renderer Utilization


如果这个值超过了~50%,就意味着你的动画可能对帧率有所限制,很可能因为离屏渲染或者是重绘导致的过度混合。


Tiler Utilization


如果这个值超过了~50%,就意味着你的动画可能限制于几何结构方面,也就是在屏幕上有太多的图层占用了。


640?wx_fmt=other


Instruments


图上面一部分是第一种方法的数据,下面一部分是第四种方法的数据。


第一种方法的Renderer Utilization 和 Tiler Utilization 基本在90%左右。帧率在20左右。


第四种方法的Renderer Utilization 和 Tiler Utilization 基本在20%左右。帧率接近60。


帧率越接近60滑动越顺畅。


但是经过跟@nerozhao的讨论发现第四种Core Graphics绘制圆角会有大量的内存占用,
  而且每次绘制的时候CUP消耗会很大。

  由于@nerozhao使用了UITableView进行测试,因为UITableView滚动的时候是一直在
  复用的,UIImageView会重复绘制,所以会一直消耗CUP,然后你就能看的明显的卡顿。

  @nerozhao在UITableView里图片的绘制在后台线程进行绘制,解决了卡顿问题,但是
  由于是在后台线程的异步绘制所以在滚动的时候会看到图片先是正方形然后再变成圆形。

  而我使用的是UIScrollerView进行的测试,只有第一次绘制的时候会占用CUP资源,
  所以滑动的时候还是挺流畅的,但是内存消耗还是很大。如果是主线程绘制的话会阻塞一
  点时间的主线程,而后台线程绘制的话内存消耗会更大,特别容易崩溃。


所以第四种方法当图片特别多的时候很容易Received memory warning导致崩溃


github 绘制圆角源码参考


NZCircularImageView

https://github.com/NZN/NZCircularImageView


640?wx_fmt=gif最后


关于研究过程及各种设置圆角方法的例子测试对比 github源码

https://github.com/walkdianzi/DSRoundedImageArticle/tree/master


 推荐↓↓↓ 

640?wx_fmt=png

?16个技术公众号】都在这里!

涵盖:程序员大咖、源码共读、程序员共读、数据结构与算法、黑客技术和网络安全、大数据科技、编程前端、Java、Python、Web编程开发、Android、iOS开发、Linux、数据库研发、幽默程序员等。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值