原文git
寫在前面github
iOS設置圓角的性能探究已是一個老生常談的問題了,衆所周知,若是直接使用layer的cornerRadius + masksToBounds雖然能夠很方便的完成圓角設置,但會引發離屏渲染,致使性能問題,在列表視圖中過多的圓角設置就會致使滑動卡頓,如今主流的方案就是在獲取圖片的一刻開啓異步線程對圖片進行相應的圓角處理,把圖片處理成想要的圖片在返回主線程進行顯示,想要便捷的達成此目的推薦YY大神的YYWebImage,其在獲取圖片的時候的時候提供了一個transform的block,在此block中你能夠完成圖片的處理工做,可是在實際的使用中,我以爲第二種方案仍是有些不方便的地方,具體以下:緩存
一、對於網絡圖片,大多數要提早設置展位圖,若是美工沒有提供圓角佔位圖片,你須要相應的對佔位圖片進行圓角處理;網絡
二、對於混合視圖須要圓角的,好比下圖,圖片上有一個Label,label也須要圓角化,你也得對label進行單獨的處理(這裏我有個小tip:若是必須使用這種方式,個人作法是生成一張左下角和右下角圓角化的黑色背景圖片,而後使用colorWithPatternImage 將圖片設置label的背景色,這樣你不須要爲這個黑色的圓角地圖另外建立一個視圖)app
圖片上有Label.png異步
三、若是多處須要重複使用同一個圖片地址,使用YYWebImage時,其會將tranform後的圖片緩存起來,因此就會出現,若是你在一個地方圓角化了該圖片,在另外一個地方使用時依然會是圓角化的圖片,這顯然在有時候是不知足需求的,不過你可使用不一樣的YYWebImageManager來管理相同圖片地址而須要不一樣transform的圖片;性能
綜上所述,雖然能夠經過一些方式解決上述問題,若是有一個性能優秀且能避免上訴問題產生的圓角化方案就更好了。spa
個人方案線程
要避免上述問題,咱們就不能從修改圖片入手了,仍是須要從視圖層次入手,我採起的方案其實也至關簡單,若是某個視圖須要圓角化,我只須要在該視圖上添加一個子layer到最上層,用於遮蓋該視圖及其子視圖,設置layer的圖片爲恰好可以遮蓋成所需圓角樣子而且圖片顏色恰好是該視圖父視圖的背景顏色就達到達到想要的效果的,因爲該遮罩layer在最上層,因此對於上面所提到的第二個缺點中的Label,也順帶着遮罩了,因此無需再次處理,固然因爲咱們是在視圖層次而非圖片層次處理的圓角,上面的第一個和第三個缺點也不存在了,這樣其實很簡單的解決的上訴三個缺點,下面來看看相關的代碼:3d
一、首先是繪製遮蓋layer的圖層圖片,固然咱們可讓美工切圖給咱們,可是若是對於每一個尺寸的視圖都去切圖的話,工做量就相應增大了,其實咱們值須要繪製一張以下的圖片,若是是空白,請點擊圖片查看
帶邊框.png
不帶邊框.png
先來看看繪製代碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
/**我建立了一個分類用於建立相應的遮罩圖片*/
@implementation UIImage (XWAddForRoundedCorner)
/**提供一個在一個指定的size中繪製圖片的便捷方法*/
+ (UIImage *)xw_imageWithSize:(CGSize)size drawBlock:(void (^)(CGContextRef context))drawBlock {
if
(!drawBlock)
return
nil;
UIGraphicsBeginImageContextWithOptions(size, NO, 0);
CGContextRef context = UIGraphicsGetCurrentContext();
if
(!context)
return
nil;
drawBlock(context);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return
image;
}
/**繪製方法的具體邏輯,遮罩圖片的邏輯是繪製一個矩形,而後在繪製一個相應的圓角矩形,而後填充矩形和圓角矩形的中間部分爲父視圖的背景色*/
+ (UIImage *)xw_maskRoundCornerRadiusImageWithColor:(UIColor *)color cornerRadii:(CGSize)cornerRadii size:(CGSize)size corners:(UIRectCorner)corners borderColor:(UIColor *)borderColor borderWidth:(CGFloat)borderWidth{
return
[UIImage xw_imageWithSize:size drawBlock:^(CGContextRef _Nonnull context) {
CGContextSetLineWidth(context, 0);
[color set];
CGRect rect = CGRectMake(0, 0, size.width, size.height);
//繪製一個矩形,這裏發-0.3是爲了防止邊緣的鋸齒,
UIBezierPath *rectPath = [UIBezierPath bezierPathWithRect:CGRectInset(rect, -0.3, -0.3)];
//繪製圓角矩形,這裏的0.3是爲了防止內邊框的鋸齒
UIBezierPath *roundPath = [UIBezierPath bezierPathWithRoundedRect:CGRectInset(rect, 0.3, 0.3) byRoundingCorners:corners cornerRadii:cornerRadii];
[rectPath appendPath:roundPath];
CGContextAddPath(context, rectPath.CGPath);
//注意要用EOFill方式進行填充而非Fill方式
CGContextEOFillPath(context);
//以下是繪製邊框,原理依舊是繪製一個外邊框而後根據邊框寬度繪製一個內邊框一樣採起EOFill的方式進行填充便可
if
(!borderColor || !borderWidth)
return
;
[borderColor set];
UIBezierPath *borderOutterPath = [UIBezierPath bezierPathWithRoundedRect:rect byRoundingCorners:corners cornerRadii:cornerRadii];
UIBezierPath *borderInnerPath = [UIBezierPath bezierPathWithRoundedRect:CGRectInset(rect, borderWidth, borderWidth) byRoundingCorners:corners cornerRadii:cornerRadii];
[borderOutterPath appendPath:borderInnerPath];
CGContextAddPath(context, borderOutterPath.CGPath);
CGContextEOFillPath(context);
}];
}
@end
|
上述的繪製方法充分利用了系統繪製圓角的方法bezierPathWithRoundedRect + EOFill,其實最開始我是考慮本身繪製四分之一的圓弧來構建圓角的,後來發現不但代碼多了很多,並且繪製出來的圓角始終沒有系統這個繪製方法的圓潤,我打印的系統的圓角路徑,發現其應該不是四分之一圓弧,發現他的控制點取值有些奇怪,我也不太清楚是如何取的,可是效果的確要好一點,總之採起如上的EOFill方式取巧,繪製的圓角圖片能夠達到和系統的cornerRadius徹底相同的效果!
二、對於繪製的圖片,咱們應該進行保存,當遇到顏色,圓角以及邊框等屬性徹底相同的繪製請求時候,咱們能夠及時複用,避免屢次建立
優缺點
優勢的話,主要是避免了去解決上面說道提到的幾個問題,若是你有遇到上面3個問題的困擾,我以爲這是至關不錯的方案
再羅列缺點:
一、因爲建立圖片須要一個背景色,該背景色源於須要遮蓋視圖的父視圖的顏色,若是該父視圖的顏色不是純色或者存在透明的話,此時該方式就不適用了,此時仍是應該採起處理圖片的方式來解決;
二、若是父視圖的顏色會變化,好比點擊cell的時候,此刻你須要同時更新遮罩圖片爲相應顏色的圖片,因此須要多寫一些代碼。
封裝
對於此方案,我封裝了一個簡單的UIView的分類UIView+XWAddForRoundedCorner來達到目的,github地址是XWCornerRadius ,此分類分紅簡單隻有3個API ,且代碼只有200行,沒有其它依賴,具體API以下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
/**
設置一個四角圓角
@param radius 圓角半徑
@param color 圓角背景色
*/
- (void)xw_roundedCornerWithRadius:(CGFloat)radius cornerColor:(UIColor *)color;
/**
設置一個普通圓角
@param radius 圓角半徑
@param color 圓角背景色
@param corners 圓角位置
*/
- (void)xw_roundedCornerWithRadius:(CGFloat)radius cornerColor:(UIColor *)color corners:(UIRectCorner)corners;
44
/**
設置一個帶邊框的圓角
44
@param cornerRadii 圓角半徑cornerRadii
@param color 圓角背景色
@param corners 圓角位置
@param borderColor 邊框顏色
@param borderWidth 邊框線寬
*/
- (void)xw_roundedCornerWithCornerRadii:(CGSize)cornerRadii cornerColor:(UIColor *)color corners:(UIRectCorner)corners borderColor:(UIColor *)borderColor borderWidth:(CGFloat)borderWidth;
|
你只須要調用相應的API就能完成一個圓角 + 邊框的遮罩效果,相同遮罩圖片的複用我也作了相關處理了,具體使用以下:
1
|
[headerView xw_roundedCornerWithCornerRadii:XWSizeMake(40, 40) cornerColor:[UIColor whiteColor] corners:UIRectCornerAllCorners borderColor:[UIColor redColor] borderWidth:widthRatio(2)];
|
demo列表.gif