55 (OC)* 图片圆角处理
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计算,但是应用范围有限。
总结
- 在可以使用混合图层遮挡的场景下,优先使用第四种方法。
- 即使是非iOS9以上系统,第一种方法在综合性能上依然强于后两者,iOS9以上由于没有了离屏渲染更是首选。
- 方法三由于需要大量计算和增加部分内存,需要实际情况各自取舍。
对图片进行圆角处理会相比于直角,它更加柔和优美,是一种很常见的视图效果,在APP中常用于对用户头像的美化,但是设置不当就会让你的APP性能下降,导致掉帧,影响用户体验
首先介绍一下常用的切圆角的几种常见方式
第一种方法:通过设置layer的属性
UIImageView *imageView = [[UIImageView alloc]initWithFrame:CGRectMake(100, 100, 100, 100)]; //只需要设置layer层的两个属性
//设置圆角
 imageView.layer.cornerRadius = imageView.frame.size.width / 2;
//将多余的部分切掉
 imageView.layer.masksToBounds = YES;
 [self.view addSubview:imageView];
但是这种方式切圆角的很大一个弊端就是影响性能,对于图片较少的情况下还不是很明显,不会太影响帧数,但是图片多的时候就会严重影响帧数(视图和圆角的大小对帧率并没有什么卵影响,数量才是伤害的核心输出),大概圆角的数量在30-40左右的时候,界面就会卡出翔的节奏,所以开发中一般不会用这个方法.
一开始我以为导致帧数下降的原因是imageView.layer.cornerRadius 这个方法造成的,后来才发现罪魁祸首是imageView.layer.masksToBounds ,这玩意会导致离屏渲染,
离屏渲染我并没有深入的去了解,在网上找资料大体的了解下
渲染机制是GPU在当前屏幕缓冲区外新开辟一个渲染缓冲区进行工作,也就是离屏渲染,这会给我们带来额外的性能损耗,如果这样的圆角操作达到一定数量,会触发缓冲区的频繁合并和上下文的的频繁切换,性能的代价会宏观地表现在用户体验上----掉帧
但是ios9之后苹果对离屏渲染做了优化
1.iOS 9.0 之前UIimageView跟UIButton设置圆角都会触发离屏渲染
2.iOS 9.0 之后UIButton设置圆角会触发离屏渲染,而UIImageView里png图片设置圆角不会触发离屏渲染了,如果设置其他阴影效果之类的还是会触发离屏渲染的。
这可能是苹果也意识到离屏渲染会产生性能问题,所以能不产生离屏渲染的地方苹果也就不用离屏渲染了。
对第一种方法的总结
1.如果图片数量很少,能够只用 cornerRadius 解决问题,就不用优化。
2.如果必须设置 masksToBounds,而且只是设置UIImageView的圆角,那么久不用考虑离屏渲染,如果设置其他的空间,那么就要在空间设置圆角过多的情况下舍弃这一种方法
第二种方法:使用贝塞尔曲线UIBezierPath和Core Graphics框架画出一个圆角
UIImageView *imageView = [[UIImageView alloc]initWithFrame:CGRectMake(100, 100, 100, 100)];
imageView.image = [UIImage imageNamed:@"1"];
//开始对imageView进行画图 UIGraphicsBeginImageContextWithOptions(imageView.bounds.size, NO, 1.0);
//使用贝塞尔曲线画出一个圆形图
[[UIBezierPath bezierPathWithRoundedRect:imageView.bounds cornerRadius:imageView.frame.size.width] addClip];
 [imageView drawRect:imageView.bounds];
 imageView.image = UIGraphicsGetImageFromCurrentImageContext();
//结束画图
UIGraphicsEndImageContext(); [self.view addSubview:imageView];
第三种方法:使用CAShapeLayer和UIBezierPath设置圆角
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad { [super viewDidLoad];
UIImageView *imageView = [[UIImageView alloc]initWithFrame:CGRectMake(100, 100, 100, 100)]; imageView.image = [UIImage imageNamed:@"1"];
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:imageView.bounds byRoundingCorners:UIRectCornerAllCorners cornerRadii:imageView.bounds.size];
 CAShapeLayer *maskLayer = [[CAShapeLayer alloc]init];
//设置大小
maskLayer.frame = imageView.bounds;
//设置图形样子
maskLayer.path = maskPath.CGPath;
 imageView.layer.mask = maskLayer;
[self.view addSubview:imageView];}
1.首先是CAShapeLayer
1.1CAShapeLayer继承于CALayer,可以使用CALayer的所有属性值;
1.2CAShapeLayer需要贝塞尔曲线配合使用才有意义(也就是说才有效果)
1.3使用CAShapeLayer(属于CoreAnimation)与贝塞尔曲线可以实现不在view的drawRect(继承于CoreGraphics走的是CPU,消耗的性能较大)方法中画出一些想要的图形
1.4CAShapeLayer动画渲染直接提交到手机的GPU当中,相较于view的drawRect方法使用CPU渲染而言,其效率极高,能大大优化内存使用情况
总的来说就是用CAShapeLayer的内存消耗少,渲染速度快,建议使用第三种
框架ZYCornerRadius
另外在给大家介绍一个第三方框架,虽然star不多,但是感觉挺使用,个人使用的不是太多,因为平时工作中大量的图片切圆角并没有遇到,
地址:https://github.com/liuzhiyi1992/ZYCornerRadius
我简单的了解了下,作者用了两种途径去实现,一个是uiimageView的分类,另外一个是子类实现,而且用到了runtime运行时的知识,(给分类中增加私有属性),总体会大大降低了设置圆角是产生的内存占用高和帧数低的问题,而且还支持多种带边框的圆角,这里就不在多赘述了,大家有需要的可以去github去克隆下来研究研究。新的url。
最后总结
1.对于圆角少的情况下,而且是ios9以上,设置图片可以不用考虑离屏渲染,数量少的阴影和其他控件的圆角设置也影响不大
2.数量多的情况,而且ios9以下的情况切忌使用cornerRadius,maskToBounds来设置,掉帧太严重,尽量使用贝塞尔曲线和Core Graphics框架或者CAShapeLayer来去实现,或者用第三方框架
55 (OC)* 图片圆角处理的更多相关文章
- OC 图片圆角实现
		self.imageTouX.layer.masksToBounds=YES; self.imageTouX.layer.cornerRadius=/2.0f; //设置为图片宽度的一半出来为圆形 s ... 
- Glide的加载图片的帮助类,用来把图片圆角或者改成圆形图片
		Glide虽然非常好用但是没找到把图片圆角的方法,所以百度了一个非常不错的加载类自己实现圆角图 感谢原文章作者:http://blog.csdn.net/weidongjian/article/det ... 
- php imagick设置图片圆角的方法
		php imagick设置图片圆角的方法 <pre>header('Content-Type: image/png'); $image = new Imagick('http://stat ... 
- 使用Picasso实现图片圆角和图片圆形
		作者很好的文章访问量缺很少也很难搜到(我这里插个眼以后用的到)作者:先知丨先觉 来源:CSDN 原文:https://blog.csdn.net/github_33304260/article/det ... 
- Android 图片圆角的设置
		ImageView的scaleType的属性有好几种,分别是matrix(默认).center.centerCrop.centerInside.fitCenter.fitEnd.fitStart.fi ... 
- Android图片圆角效果
		一般来说图片加圆角可以使用 Java 的方式来进行, 对图片略加处理即可, 但也可以使用纯XML+Nice-Patch图片来进行, 这样的速度会更快. 如果背景是纯色的情况下建议使用此方法. 原理则是 ... 
- Photoshop-制作图片圆角2种方法[转]
		方案一: 使用选区和蒙版相结合,用图章制作圆角选区,删除多余部分 效果: 实现步骤: 一.如果是直接在已有的图片上面编辑则看下图,否则跳过此不 二.用矩形工具选择需要保留的图片内容 三.选 ... 
- 关于在css里设置图片圆角的问题
		今天做了一个项目,效果图内页的产品图片都是带圆角的,于是前端的做了圆角的效果,div+css是这样的,首先div布局是: <div class="tiandi_item" o ... 
- Css3图片圆角,兼容所有浏览器
		<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ... 
随机推荐
- Springboot源码分析之项目结构
			Springboot源码分析之项目结构 摘要: 无论是从IDEA还是其他的SDS开发工具亦或是https://start.spring.io/ 进行解压,我们都会得到同样的一个pom.xml文件 4. ... 
- 『深度应用』NLP机器翻译深度学习实战课程·壹(RNN base)
			深度学习用的有一年多了,最近开始NLP自然处理方面的研发.刚好趁着这个机会写一系列NLP机器翻译深度学习实战课程. 本系列课程将从原理讲解与数据处理深入到如何动手实践与应用部署,将包括以下内容:(更新 ... 
- ts 学习笔记 - 泛型
			目录 泛型 举个栗子 泛型约束 多个参数时也可以在泛型约束中使用类型参数 泛型接口 泛型类 泛型参数的默认类型 泛型 泛型(Generics)是指在定义函数.接口或者类的时候, 不预先指定其类型,而是 ... 
- .net打杂工程师的面试感想和总结
			上个月26号辞职了,今天开始第一场面试,随便写写感想,后面还会继续分享一些感想 前言 这个时候找工作是不是找死? 开门见山吧,95年的,之前做过两份工作,第一家公司在做了2年2个月,在北京,也就是去年 ... 
- NDK Cmake
			CMake与NDK搭配使用时,可以配置的部分变量: 1. `ANDROID_PLATFORM`:指定Android的目标版本,对应`$NDK/platforms/`目录下的版本.通常情况下是`defa ... 
- Git随身手册
			Git随身手册 本文是关于Git探索的一篇文章,阐述了Git的大部分命令和使用方式,并列举了几个典型的使用场景以供参考和体会. 对于Git这个分布式的VCS,从链表的角度来看待是最容易理解的: 一次c ... 
- 林大妈的CSS知识清单(一)添加样式
			回顾CSS选择符,学习接入样式的更多方式. 一.选择符 1. 种类 ① 类型选择符:直接的HTML标签名,例如: body.p.div 等: ② 后代选择符:空格,例如: div p 选择div中的所 ... 
- vue-小爱ADMIN系列文章(二):微信微博等分享,国际化,前端性能优化,nginx服务器部署
			最近在做我的小爱ADMIN后台管理系统,结合当前市场后台管理系统对相关功能的需求,我又开始新增了一些新的功能和组件,如分享功能组件,项目国际化功能:项目完成后,部署在nginx服务器,发现首次访问的速 ... 
- egret之消除游戏开发
			1.地图 (1)地图形状不同,尺寸不变 (2)背景图变化 2.步数 (1)不同关卡步数不同 (2)步数为01,游戏失败 3.道具 4.消除 (1)>=3可消除 (2)不可消除时,自动打乱 5.数 ... 
- three.js实现球体地球城市模拟迁徙
			概况如下:1.SphereGeometry实现自转的地球:2.THREE.ImageUtils.loadTexture加载地图贴图材质:3.THREE.Math.degToRad,Math.sin,M ... 
