本文一共介紹 5 種方法裁剪圓角,并且分析其利弊
目錄
- cornerRadius + masksToBounds
- 在文本視圖類上實(shí)現(xiàn)圓角
- UILabel
- UITextField
- UITextView
- 混合圖層
- Quartz2D
- Mask 遮罩實(shí)現(xiàn)
序言
在實(shí)際開發(fā)項(xiàng)目中,有很多場景需要裁剪圓角艰躺,如果實(shí)現(xiàn)方式不當(dāng)哑姚,對性能會造成很大影響陷揪,本文就針對如何裁剪圓角做一個(gè)匯總泪姨。
一 cornerRadius + masksToBounds
設(shè)置圓角:
view.layer.cornerRadius = 5
文檔中cornerRadius屬性的說明:
Setting the radius to a value greater than 0.0 causes the layer to begin drawing rounded corners on its background. By default, the corner radius does not apply to the image in the layer’s contents property; it applies only to the background color and border of the layer. However, setting the masksToBounds property to YES causes the content to be clipped to the rounded corners.
很明了碰逸,只對前景框和背景色起作用肠槽,再看 CALayer 的結(jié)構(gòu)擎淤,如果contents有內(nèi)容或者內(nèi)容的背景不是透明的話,還需要把這部分弄個(gè)角出來秸仙,不然合成的結(jié)果還是沒有圓角嘴拢,所以才要修改masksToBounds為true(在 UIView 上對應(yīng)的屬性是clipsToBounds,在 IB 里對應(yīng)的設(shè)置是「Clip Subiews」選項(xiàng))筋栋。前些日子很熱鬧的圓角優(yōu)化文章中的2篇指出是修改masksToBounds為true而非修改cornerRadius才是觸發(fā)離屏渲染的原因炊汤,但如果以「Color Offscreen-Renderd Yellow」的特征為標(biāo)準(zhǔn)的話,
這兩個(gè)屬性單獨(dú)作用時(shí)都不是引發(fā)離屏渲染的原因,他倆合體(masksToBounds = true, cornerRadius>0)才是抢腐。
- 實(shí)例代碼
// 裁剪圓角
- (void)clipRoundedCornerImage {
// 圖片
UIImageView *iconImgV = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
iconImgV.image = [UIImage imageNamed:@"icon_girl"];
iconImgV.layer.cornerRadius = 100;
iconImgV.layer.masksToBounds = YES;
[self.view addSubview:iconImgV];
[iconImgV mas_makeConstraints:^(MASConstraintMaker *make) {
make.size.mas_equalTo(iconImgV.size);
make.top.equalTo(self.view.mas_top).offset(100);
make.centerX.equalTo(self.view);
}];
// 視圖
UIView *redView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
redView.backgroundColor = [UIColor redColor];
redView.layer.cornerRadius = 100;
redView.layer.masksToBounds = YES;
[self.view addSubview:redView];
[redView mas_makeConstraints:^(MASConstraintMaker *make) {
make.size.mas_equalTo(redView.size);
make.top.equalTo(iconImgV.mas_bottom).offset(50);
make.centerX.equalTo(self.view);
}];
}
運(yùn)行結(jié)果
總結(jié):這種方法最簡潔姑曙,代碼量最少,但是性能最低迈倍,因?yàn)闀|發(fā)
離屏渲染
伤靠。對性能要求不是很高的場合可以使用。
二 在文本視圖類上實(shí)現(xiàn)圓角
文本視圖主要是這三類:UILabel, UITextField, UITextView啼染。其中 UITextField 類自帶圓角風(fēng)格的外型宴合,UILabel 和 UITextView 要想顯示圓角需要表現(xiàn)出與周圍不同的背景色才行。想要在 UILabel 和 UITextView 上實(shí)現(xiàn)低成本的圓角(不觸發(fā)離屏渲染)迹鹅,需要保證 layer 的contents呈現(xiàn)透明的背景色卦洽,文本視圖類的 layer 的contents默認(rèn)是透明的(字符就在這個(gè)透明的環(huán)境里繪制、顯示)斜棚,此時(shí)只需要設(shè)置 layer 的backgroundColor阀蒂,再加上cornerRadius就可以搞定了。不過 UILabel 上設(shè)置backgroundColor的行為被更改了弟蚀,不再是設(shè)定 layer 的背景色而是為contents設(shè)置背景色蚤霞,UITextView 則沒有改變這一點(diǎn)。
2.1 UILabel
- 實(shí)例代碼如下
- (void)drawUI {
UILabel *radiusLbe = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 200, 44)];
radiusLbe.textColor = [UIColor blackColor];
radiusLbe.text = @"裁剪圓角";
radiusLbe.textAlignment = NSTextAlignmentCenter;
// a裁剪圓角
radiusLbe.layer.backgroundColor = [[UIColor orangeColor] CGColor];
radiusLbe.layer.cornerRadius = 10;
[self.view addSubview:radiusLbe];
}
效果如下
總結(jié):UILabel設(shè)置圓角時(shí)义钉,只需要設(shè)置
cornerRadius
的值即可昧绣,不會觸發(fā)離屏渲染
。
2.2 UITextField
- 實(shí)例代碼如下
- (void)drawTextF {
UITextField *textF = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, 200, 44)];
textF.textColor = [UIColor blackColor];
textF.text = @"裁剪圓角";
textF.textAlignment = NSTextAlignmentCenter;
// a裁剪圓角
textF.layer.backgroundColor = [[UIColor greenColor] CGColor];
textF.layer.cornerRadius = 10;
[self.view addSubview:textF];
}
運(yùn)行效果如下
總結(jié):UITextField設(shè)置圓角時(shí)捶闸,只需要設(shè)置
cornerRadius
的值即可夜畴,不會觸發(fā)離屏渲染
。
2.3 UITextView
- 實(shí)例代碼如下
- (void)drawTextV {
UITextView *textV = [[UITextView alloc] initWithFrame:CGRectMake(0, 0, 20, 200)];
textV.textColor = [UIColor whiteColor];
textV.text = @"如何在UITextView視圖上實(shí)現(xiàn)圓角?\n如何在UITextView視圖上實(shí)現(xiàn)圓角?\n如何在UITextView視圖上實(shí)現(xiàn)圓角?\n如何在UITextView視圖上實(shí)現(xiàn)圓角?\n如何在UITextView視圖上實(shí)現(xiàn)圓角?\n如何在UITextView視圖上實(shí)現(xiàn)圓角?\n如何在UITextView視圖上實(shí)現(xiàn)圓角?\n如何在UITextView視圖上實(shí)現(xiàn)圓角?\n如何在UITextView視圖上實(shí)現(xiàn)圓角?\n如何在UITextView視圖上實(shí)現(xiàn)圓角?\n如何在UITextView視圖上實(shí)現(xiàn)圓角?\n如何在UITextView視圖上實(shí)現(xiàn)圓角?\n如何在UITextView視圖上實(shí)現(xiàn)圓角?\n如何在UITextView視圖上實(shí)現(xiàn)圓角?\n如何在UITextView視圖上實(shí)現(xiàn)圓角?\n";
textV.textAlignment = NSTextAlignmentCenter;
// a裁剪圓角
textV.layer.backgroundColor = [[UIColor blueColor] CGColor];
textV.layer.cornerRadius = 10;
[self.view addSubview:textV];
}
運(yùn)行效果如下:
總結(jié):UITextView設(shè)置圓角時(shí)鉴嗤,只需要設(shè)置
cornerRadius
的值即可斩启,不會觸發(fā)離屏渲染
序调。
三 混合圖層
我們可以在需要裁剪的視圖上面添加一個(gè)已經(jīng)裁剪好的視圖醉锅,達(dá)到顯示圓角的效果》⒕睿可以讓美工提供一張已經(jīng)是圓角的圖片硬耍,但是局限性比較大,因?yàn)槌叽绻潭ā?/p>
下面介紹如何動態(tài)生成一個(gè)任意尺寸边酒,任意圓角的遮罩圖片
3.1 裁剪成圓角
- 實(shí)例代碼如下
- (void)drawRoundedCornerImage {
UIImageView *iconImgV = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
iconImgV.image = [UIImage imageNamed:@"icon"];
[self.view addSubview:iconImgV];
[iconImgV mas_makeConstraints:^(MASConstraintMaker *make) {
make.size.mas_equalTo(iconImgV.size);
make.top.equalTo(self.view.mas_top).offset(500);
make.centerX.equalTo(self.view);
}];
UIImageView *imgView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
[self.view addSubview:imgView];
[imgView mas_makeConstraints:^(MASConstraintMaker *make) {
make.size.mas_equalTo(imgView.size);
make.top.equalTo(iconImgV.mas_top);
make.leading.equalTo(iconImgV.mas_leading);
}];
// 圓形
imgView.image = [self drawCircleRadius:100 outerSize:CGSizeMake(200, 200) fillColor:[UIColor whiteColor]];
}
// 繪制圓形
- (UIImage *)drawCircleRadius:(float)radius outerSize:(CGSize)outerSize fillColor:(UIColor *)fillColor {
UIGraphicsBeginImageContextWithOptions(outerSize, false, [UIScreen mainScreen].scale);
// 1经柴、獲取當(dāng)前上下文
CGContextRef contextRef = UIGraphicsGetCurrentContext();
//2.描述路徑
// ArcCenter:中心點(diǎn) radius:半徑 startAngle起始角度 endAngle結(jié)束角度 clockwise:是否逆時(shí)針
UIBezierPath *bezierPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(outerSize.width * 0.5, outerSize.height * 0.5) radius:radius startAngle:0 endAngle:M_PI * 2 clockwise:NO];
[bezierPath closePath];
// 3.外邊
[bezierPath moveToPoint:CGPointMake(0, 0)];
[bezierPath addLineToPoint:CGPointMake(outerSize.width, 0)];
[bezierPath addLineToPoint:CGPointMake(outerSize.width, outerSize.height)];
[bezierPath addLineToPoint:CGPointMake(0, outerSize.height)];
[bezierPath addLineToPoint:CGPointMake(0, 0)];
[bezierPath closePath];
//4.設(shè)置顏色
[fillColor setFill];
[bezierPath fill];
CGContextDrawPath(contextRef, kCGPathStroke);
UIImage *antiRoundedCornerImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return antiRoundedCornerImage;
}
效果如下
布局如下圖所示
總結(jié):這種方法是通過上面添加一層裁剪成圓形圖片蒙層達(dá)到
欺騙
用戶的效果,這種方法不會造成離屏渲染
墩朦,性能較高坯认。
3.2 裁剪任意角度
上面 3.1 是只能裁剪成圓角,擴(kuò)展性不是很強(qiáng),下面的方法可以裁剪成任意角度的圖片牛哺,擴(kuò)展性很強(qiáng)
- 實(shí)例代碼如下
/**
繪制裁剪圓角后圖片
@param radius 圓角
@param rectSize 視圖尺寸
@param fillColor 填充色
@return 圖片
*/
- (UIImage *)drawAntiRoundedCornerImageWithRadius:(float)radius rectSize:(CGSize)rectSize fillColor:(UIColor *)fillColor {
UIGraphicsBeginImageContextWithOptions(rectSize, false, [UIScreen mainScreen].scale);
CGContextRef contextRef = UIGraphicsGetCurrentContext();
//2.描述路徑
UIBezierPath *bezierPath = [UIBezierPath bezierPath];
CGPoint hLeftUpPoint = CGPointMake(radius, 0);
CGPoint hRightUpPoint = CGPointMake(rectSize.width - radius, 0);
CGPoint hLeftDownPoint = CGPointMake(radius, rectSize.height);
CGPoint vLeftUpPoint = CGPointMake(0, radius);
CGPoint vRightDownPoint = CGPointMake(rectSize.width, rectSize.height - radius);
CGPoint centerLeftUp = CGPointMake(radius, radius);
CGPoint centerRightUp = CGPointMake(rectSize.width - radius, radius);
CGPoint centerLeftDown = CGPointMake(radius, rectSize.height - radius);
CGPoint centerRightDown = CGPointMake(rectSize.width - radius, rectSize.height - radius);
[bezierPath moveToPoint:hLeftUpPoint];
[bezierPath addLineToPoint:hRightUpPoint];
[bezierPath addArcWithCenter:centerRightUp radius:radius startAngle:(CGFloat)(M_PI * 3 / 2) endAngle:(CGFloat)(M_PI * 2) clockwise: true];
[bezierPath addLineToPoint:vRightDownPoint];
[bezierPath addArcWithCenter:centerRightDown radius: radius startAngle: 0 endAngle: (CGFloat)(M_PI / 2) clockwise: true];
[bezierPath addLineToPoint:hLeftDownPoint];
[bezierPath addArcWithCenter:centerLeftDown radius: radius startAngle: (CGFloat)(M_PI / 2) endAngle: (CGFloat)(M_PI) clockwise: true];
[bezierPath addLineToPoint:vLeftUpPoint];
[bezierPath addArcWithCenter:centerLeftUp radius: radius startAngle: (CGFloat)(M_PI) endAngle: (CGFloat)(M_PI * 3 / 2) clockwise: true];
[bezierPath addLineToPoint:hLeftUpPoint];
[bezierPath closePath];
//If draw drection of outer path is same with inner path, final result is just outer path.
[bezierPath moveToPoint:CGPointZero];
[bezierPath addLineToPoint:CGPointMake(0, rectSize.height)];
[bezierPath addLineToPoint:CGPointMake(rectSize.width, rectSize.height)];
[bezierPath addLineToPoint:CGPointMake(rectSize.width, 0)];
[bezierPath addLineToPoint:CGPointZero];
[bezierPath closePath];
[fillColor setFill];
[bezierPath fill];
CGContextDrawPath(contextRef, kCGPathFillStroke);
UIImage *antiRoundedCornerImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return antiRoundedCornerImage;
}
運(yùn)行效果如下: 角度值為50陋气,填充色分別為綠色,白色時(shí)效果
運(yùn)行效果如下: 角度值為100引润,填充色分別為綠色巩趁,白色時(shí)效果 (圓形)
總結(jié):這種方法擴(kuò)展性很強(qiáng),可以裁剪任意角度淳附,性能很高议慰,推薦使用。
四 Quartz2D
通過Quartz2D將圖形繪制出一張圓形圖片來進(jìn)行顯示奴曙。
- 實(shí)例代碼如下
- (UIImage *)circleImage {
//1.開啟圖片圖形上下文:注意設(shè)置透明度為非透明
UIGraphicsBeginImageContextWithOptions(self.size, NO, 0.0);
//2.開啟圖形上下文
CGContextRef ref = UIGraphicsGetCurrentContext();
//3.繪制圓形區(qū)域(此處根據(jù)寬度來設(shè)置)
CGRect rect = CGRectMake(0, 0, self.size.width, self.size.width);
CGContextAddEllipseInRect(ref, rect);
//4.裁剪繪圖區(qū)域
CGContextClip(ref);
//5.繪制圖片
[self drawInRect:rect];
//6.獲取圖片
UIImage * image = UIGraphicsGetImageFromCurrentImageContext();
//7.關(guān)閉圖形上下文
UIGraphicsEndImageContext();
return image;
}
運(yùn)行效果如下:
總結(jié):這種方法性能也較高别凹,只是局限性比較大,只能針對圖片進(jìn)行裁剪洽糟。我們一般可以將該方法添加到
UIImage
的分類中
@interface UIImage (CSCircleImage)
- (UIImage *)circleImage;
@end
五 Mask
5.1 使用圖片
Mask 效果與混合圖層的效果非常相似番川,只是使用同一個(gè)遮罩圖像時(shí),mask 與混合圖層的效果是相反的脊框,在 Demo 里使用反向內(nèi)容的遮罩來實(shí)現(xiàn)圓角颁督。實(shí)現(xiàn) mask 效果使用 CALayer 的layer屬性,在 iOS 8 以上可以使用 UIView 的maskView屬性浇雹。
- (void)drawUI {
UIImageView *iconImgV = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 80, 80)];
iconImgV.image = [UIImage imageNamed:@"icon_girl"];
[self.view addSubview:iconImgV];
[iconImgV mas_makeConstraints:^(MASConstraintMaker *make) {
make.size.mas_equalTo(iconImgV.size);
make.centerY.equalTo(self.view);
make.centerX.equalTo(self.view);
}];
// 設(shè)置Mask
if (@available(iOS 8.0, *)) {
iconImgV.maskView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"RoundMask"]];
} else {
CALayer *maskLayer = [[CALayer alloc] init];
maskLayer.frame = iconImgV.bounds;
maskLayer.contents = (__bridge id _Nullable)([[UIImage imageNamed:@"RoundMask"] CGImage]);
iconImgV.layer.mask = maskLayer;
}
}
運(yùn)行效果如下:
備注:其中icon_girl.png
和RoundMask
分別為下面兩張圖片
如果所有 maskImage 相同的話沉御,使用一個(gè) maskImage 就夠了,不然每次生成一個(gè)新的 UIImage 也會是一個(gè)性能隱患點(diǎn)昭灵。注意:可以使用同一個(gè) maskImage吠裆,但不能使用同一個(gè) maskView,不然同時(shí)只會有一個(gè) mask 效果烂完。
5.2 使用 CAShapeLayer 來指定混合的路徑试疙。
使用 mask 來實(shí)現(xiàn)圓角時(shí)也可以不用圖片,而使用 CAShapeLayer 來指定混合的路徑抠蚣。
UIBezierPath *roundedRectPath = [UIBezierPath bezierPathWithRoundedRect:iconImgV.bounds byRoundingCorners:UIRectCornerAllCorners cornerRadii:CGSizeMake(40, 40)];
CAShapeLayer *shapLayer = [[CAShapeLayer alloc] init];
shapLayer.path = roundedRectPath.CGPath;
iconImgV.layer.mask = shapLayer;
運(yùn)行效果如下:
我們通過設(shè)置
cornerRadii
值來設(shè)置角度值祝旷,如果為圓角,則為圖片尺寸寬高的一半即可嘶窄。
使用 Mask 裁剪圓角會造成離屏渲染怀跛,掉幀,卡頓柄冲。
本文參考
iOS離屏渲染優(yōu)化
該文章為作者原創(chuàng)吻谋,如果轉(zhuǎn)載,請注明出處