對(duì)圖片進(jìn)行圓角處理會(huì)相比于直角固翰,它更加柔和優(yōu)美,是一種很常見的視圖效果浊猾,在APP中常用于對(duì)用戶頭像的美化,但是設(shè)置不當(dāng)就會(huì)讓你的APP性能下降,導(dǎo)致掉幀,影響用戶體驗(yàn)
首先介紹一下常用的切圓角的幾種常見方式
第一種方法:通過(guò)設(shè)置layer的屬性
UIImageView *imageView = [[UIImageView alloc]initWithFrame:CGRectMake(100, 100, 100, 100)]; //只需要設(shè)置layer層的兩個(gè)屬性
//設(shè)置圓角
imageView.layer.cornerRadius = imageView.frame.size.width / 2;
//將多余的部分切掉
imageView.layer.masksToBounds = YES;
[self.view addSubview:imageView];
但是這種方式切圓角的很大一個(gè)弊端就是影響性能,對(duì)于圖片較少的情況下還不是很明顯,不會(huì)太影響幀數(shù),但是圖片多的時(shí)候就會(huì)嚴(yán)重影響幀數(shù)(視圖和圓角的大小對(duì)幀率并沒(méi)有什么卵影響沼头,數(shù)量才是傷害的核心輸出),大概圓角的數(shù)量在30-40左右的時(shí)候,界面就會(huì)卡出翔的節(jié)奏,所以開發(fā)中一般不會(huì)用這個(gè)方法.
一開始我以為導(dǎo)致幀數(shù)下降的原因是imageView.layer.cornerRadius 這個(gè)方法造成的,后來(lái)才發(fā)現(xiàn)罪魁禍?zhǔn)资莍mageView.layer.masksToBounds ,這玩意會(huì)導(dǎo)致離屏渲染,
離屏渲染我并沒(méi)有深入的去了解,在網(wǎng)上找資料大體的了解下
渲染機(jī)制是GPU在當(dāng)前屏幕緩沖區(qū)外新開辟一個(gè)渲染緩沖區(qū)進(jìn)行工作羹令,也就是離屏渲染傻谁,這會(huì)給我們帶來(lái)額外的性能損耗孝治,如果這樣的圓角操作達(dá)到一定數(shù)量,會(huì)觸發(fā)緩沖區(qū)的頻繁合并和上下文的的頻繁切換审磁,性能的代價(jià)會(huì)宏觀地表現(xiàn)在用戶體驗(yàn)上----掉幀
但是ios9之后蘋果對(duì)離屏渲染做了優(yōu)化
1.iOS 9.0 之前UIimageView跟UIButton設(shè)置圓角都會(huì)觸發(fā)離屏渲染
2.iOS 9.0 之后UIButton設(shè)置圓角會(huì)觸發(fā)離屏渲染谈飒,而UIImageView里png圖片設(shè)置圓角不會(huì)觸發(fā)離屏渲染了,如果設(shè)置其他陰影效果之類的還是會(huì)觸發(fā)離屏渲染的态蒂。
這可能是蘋果也意識(shí)到離屏渲染會(huì)產(chǎn)生性能問(wèn)題杭措,所以能不產(chǎn)生離屏渲染的地方蘋果也就不用離屏渲染了。
對(duì)第一種方法的總結(jié)
1.如果圖片數(shù)量很少,能夠只用 cornerRadius 解決問(wèn)題钾恢,就不用優(yōu)化手素。
2.如果必須設(shè)置 masksToBounds,而且只是設(shè)置UIImageView的圓角,那么久不用考慮離屏渲染,如果設(shè)置其他的空間,那么就要在空間設(shè)置圓角過(guò)多的情況下舍棄這一種方法
第二種方法:使用貝塞爾曲線UIBezierPath和Core Graphics框架畫出一個(gè)圓角
UIImageView *imageView = [[UIImageView alloc]initWithFrame:CGRectMake(100, 100, 100, 100)];
imageView.image = [UIImage imageNamed:@"1"];
//開始對(duì)imageView進(jìn)行畫圖 UIGraphicsBeginImageContextWithOptions(imageView.bounds.size, NO, 1.0);
//使用貝塞爾曲線畫出一個(gè)圓形圖
[[UIBezierPath bezierPathWithRoundedRect:imageView.bounds cornerRadius:imageView.frame.size.width] addClip];
[imageView drawRect:imageView.bounds];
imageView.image = UIGraphicsGetImageFromCurrentImageContext();
//結(jié)束畫圖
UIGraphicsEndImageContext(); [self.view addSubview:imageView];
第三種方法:使用CAShapeLayer和UIBezierPath設(shè)置圓角
#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];
//設(shè)置大小
maskLayer.frame = imageView.bounds;
//設(shè)置圖形樣子
maskLayer.path = maskPath.CGPath;
imageView.layer.mask = maskLayer;
[self.view addSubview:imageView];}
1.首先是CAShapeLayer
1.1CAShapeLayer繼承于CALayer,可以使用CALayer的所有屬性值瘩蚪;
1.2CAShapeLayer需要貝塞爾曲線配合使用才有意義(也就是說(shuō)才有效果)
1.3使用CAShapeLayer(屬于CoreAnimation)與貝塞爾曲線可以實(shí)現(xiàn)不在view的drawRect(繼承于CoreGraphics走的是CPU,消耗的性能較大)方法中畫出一些想要的圖形
1.4CAShapeLayer動(dòng)畫渲染直接提交到手機(jī)的GPU當(dāng)中泉懦,相較于view的drawRect方法使用CPU渲染而言,其效率極高疹瘦,能大大優(yōu)化內(nèi)存使用情況
總的來(lái)說(shuō)就是用CAShapeLayer的內(nèi)存消耗少,渲染速度快,建議使用第三種
框架ZYCornerRadius
另外在給大家介紹一個(gè)第三方框架,雖然star不多,但是感覺挺使用,個(gè)人使用的不是太多,因?yàn)槠綍r(shí)工作中大量的圖片切圓角并沒(méi)有遇到,
地址:https://github.com/liuzhiyi1992/ZYCornerRadius
我簡(jiǎn)單的了解了下,作者用了兩種途徑去實(shí)現(xiàn),一個(gè)是uiimageView的分類,另外一個(gè)是子類實(shí)現(xiàn),而且用到了runtime運(yùn)行時(shí)的知識(shí),(給分類中增加私有屬性),總體會(huì)大大降低了設(shè)置圓角是產(chǎn)生的內(nèi)存占用高和幀數(shù)低的問(wèn)題,而且還支持多種帶邊框的圓角,這里就不在多贅述了,大家有需要的可以去github去克隆下來(lái)研究研究
最后總結(jié)
1.對(duì)于圓角少的情況下,而且是ios9以上,設(shè)置圖片可以不用考慮離屏渲染,數(shù)量少的陰影和其他控件的圓角設(shè)置也影響不大
2.數(shù)量多的情況,而且ios9以下的情況切忌使用cornerRadius,maskToBounds來(lái)設(shè)置,掉幀太嚴(yán)重,盡量使用貝塞爾曲線和Core Graphics框架或者CAShapeLayer來(lái)去實(shí)現(xiàn),或者用第三方框架