对图片进行圆角处理会相比于直角,它更加柔和优美,是一种很常见的视图效果,在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去克隆下来研究研究
最后总结
1.对于圆角少的情况下,而且是ios9以上,设置图片可以不用考虑离屏渲染,数量少的阴影和其他控件的圆角设置也影响不大
2.数量多的情况,而且ios9以下的情况切忌使用cornerRadius,maskToBounds来设置,掉帧太严重,尽量使用贝塞尔曲线和Core Graphics框架或者CAShapeLayer来去实现,或者用第三方框架