對(duì)于目前手機(jī)的性能來(lái)說(shuō),對(duì)于少量圓角設(shè)置帶來(lái)的離屏渲染問(wèn)題凭语,帶來(lái)的卡頓問(wèn)題已經(jīng)并不是那么的明顯了贝乎,但是對(duì)于一些類型的app來(lái)說(shuō),項(xiàng)目中會(huì)有大量的圓角設(shè)置叽粹,同時(shí)又對(duì)性能要求比較高的話览效,那么避免圓角設(shè)置帶來(lái)的性能問(wèn)題,還是有必要解決的虫几。
iOS設(shè)置圓角方法以及指定位置設(shè)圓角
iOS圓角設(shè)置的幾種方法的對(duì)比以及性能檢測(cè)
上面兩篇文章中介紹了iOS常見(jiàn)的設(shè)置圓角的方法锤灿,以及對(duì)應(yīng)方法性能的檢測(cè)??。
然而在實(shí)際項(xiàng)目中仍然有其他問(wèn)題存在辆脸,也就是前面的總結(jié)仍有不足但校,下面提出兩個(gè)遇到的問(wèn)題
針對(duì)Masonry
布局的控件,如何進(jìn)行圓角設(shè)置啡氢?
其實(shí)解決方案非常簡(jiǎn)單状囱,調(diào)用layoutIfNeeded
函數(shù)進(jìn)行布局,然后所約束的控件才會(huì)按照約束條件倘是,生成當(dāng)前布局相應(yīng)的frame
和bounds
亭枷,那么就可以進(jìn)行各種樣式的圓角設(shè)置
針對(duì)四個(gè)不同的角,設(shè)置不同的半徑搀崭,如何操作叨粘?
方法
實(shí)際上與之前文章里所描述的一種方法混合圖層一個(gè)道理。在所需切圓角的控件上加一層瘤睹,利用貝塞爾切割出不同角所需要的圓角半徑
思路
如上圖所示升敲,標(biāo)記了幾個(gè)點(diǎn),進(jìn)行畫圓弧以及連線轰传,具體代碼如下
[bezierPath moveToPoint:self.hLeftUpPoint];
[bezierPath addLineToPoint:self.hRightUpPoint];
[bezierPath addArcWithCenter:self.centerRightUp radius:self.radius_TopRight ?: self.radius startAngle:(CGFloat)(M_PI * 3 / 2) endAngle:(CGFloat)(M_PI * 2) clockwise: true];
[bezierPath addLineToPoint:self.vRightDownPoint];
[bezierPath addArcWithCenter:self.centerRightDown radius:self.radius_BottomRight ?: self.radius startAngle: 0 endAngle: (CGFloat)(M_PI / 2) clockwise: true];
[bezierPath addLineToPoint:self.hLeftDownPoint];
[bezierPath addArcWithCenter:self.centerLeftDown radius:self.radius_BottomLeft ?: self.radius startAngle: (CGFloat)(M_PI / 2) endAngle: (CGFloat)(M_PI) clockwise: true];
[bezierPath addLineToPoint:self.vLeftUpPoint];
[bezierPath addArcWithCenter:self.centerLeftUp radius:self.radius_TopLeft ?: self.radius startAngle: (CGFloat)(M_PI) endAngle: (CGFloat)(M_PI * 3 / 2) clockwise: true];
[bezierPath addLineToPoint:self.hLeftUpPoint];
[bezierPath closePath];
[bezierPath moveToPoint:CGPointZero];
[bezierPath addLineToPoint:CGPointMake(0, self.rectSize.height)];
[bezierPath addLineToPoint:CGPointMake(self.rectSize.width, self.rectSize.height)];
[bezierPath addLineToPoint:CGPointMake(self.rectSize.width, 0)];
[bezierPath addLineToPoint:CGPointZero];
[bezierPath closePath];
上面的代碼具體是切割圓角驴党,具體圓角半徑與對(duì)應(yīng)點(diǎn)的位置有關(guān),合理的利用上圖所描述的點(diǎn)的坐標(biāo)获茬,可以切割出不同的弧度港庄,同時(shí)利用位移枚舉UIRectCorner
可實(shí)現(xiàn)任意一個(gè)或多個(gè)角的切割。
具體代碼
第一步: 新增了一個(gè)UIVIew+CornerRadius
的分類锦茁,.h
文件里聲明了兩種切割圓角的方法攘轩。
@interface UIView (CornerRadius)
/// 繪制裁剪圓角,可選任意角進(jìn)行切割
/// @param radius 圓角半徑
/// @param fillColor 填充色
/// @param type 裁剪角
- (void)drawCircularBeadImageWithRadius:(float)radius fillColor:(UIColor *)fillColor CornerStyle:(UIRectCorner)type;
/// 繪制裁剪圓角码俩,可選任意角進(jìn)行切割度帮,且每個(gè)角可選切割任意半徑
/// @param radius_TL 左上角半徑
/// @param radius_TR 右上角半徑
/// @param radius_BL 左下角半徑
/// @param radius_BR 右下角半徑
/// @param fillColor 填充色
- (void)drawCircularBeadImageWithRadius_TL:(float)radius_TL
radius_TR:(float)radius_TR
radius_BL:(float)radius_BL
radius_BR:(float)radius_BR
fillColor:(UIColor *)fillColor;
@end
具體的實(shí)現(xiàn):
@implementation UIView (CornerRadius)
- (void)drawCircularBeadImageWithRadius:(float)radius fillColor:(UIColor *)fillColor CornerStyle:(UIRectCorner)type {
UIImageView *imgView = [[UIImageView alloc] init];
[self addSubview:imgView];
[imgView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.top.width.height.equalTo(self);
}];
[self layoutIfNeeded];
imgView.image = [ZBCornerRadiusTool drawAntiRoundedCornerImageWithRadius:radius rectSize:imgView.frame.size fillColor:fillColor cornerStyle:type];
}
- (void)drawCircularBeadImageWithRadius_TL:(float)radius_TL
radius_TR:(float)radius_TR
radius_BL:(float)radius_BL
radius_BR:(float)radius_BR
fillColor:(UIColor *)fillColor {
UIImageView *imgView = [[UIImageView alloc] init];
[self addSubview:imgView];
[imgView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.top.width.height.equalTo(self);
}];
[self layoutIfNeeded];
imgView.image = [ZBCornerRadiusTool drawAntiRoundedCornerWithRadius_TL:radius_TL radius_TR:radius_TR radius_BL:radius_BL radius_BR:radius_BR rectSize:imgView.frame.size fillColor:fillColor];
}
@end
從上面代碼可以看出,在這里我們?cè)谛枰懈顖A角的控件上,加了一層UIImageView
笨篷,同時(shí)這里利用Masonry
對(duì)新增的圖層進(jìn)行了約束瞳秽,最重要的一句代碼[self layoutIfNeeded];
千萬(wàn)不能少,這句話率翅,就解決了上面所說(shuō)的第一個(gè)問(wèn)題练俐。
這里代碼均是針對(duì)利用Masonry
布局約束的控件去實(shí)現(xiàn)圓角,如果是利用frame
的也非常方面冕臭,直接利用控件的size
便可腺晾。筆者只提供一下思路
第二步: 添加兩個(gè)工具類,先看ZBCornerRadiusTool
類
/// 繪制裁剪圓角后圖片
/// @param radius 圓角
/// @param rectSize 視圖尺寸
/// @param fillColor 填充色
/// @param cornerStyle 圓角位置
+ (UIImage *)drawAntiRoundedCornerImageWithRadius:(float)radius rectSize:(CGSize)rectSize fillColor:(UIColor *)fillColor cornerStyle:(UIRectCorner)cornerStyle {
UIGraphicsBeginImageContextWithOptions(rectSize, false, [UIScreen mainScreen].scale);
CGContextRef contextRef = UIGraphicsGetCurrentContext();
UIBezierPath *bezierPath = [UIBezierPath bezierPath];
[ZBCornerRadiusTool configPathWithBezierPath:&bezierPath Radius:radius rectSize:rectSize cornerStyle:cornerStyle fillColor:fillColor];
CGContextDrawPath(contextRef, kCGPathFillStroke);
UIImage *antiRoundedCornerImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return antiRoundedCornerImage;
}
/// 繪制裁剪圓角后圖片
/// @param radius_TL 左上角半徑
/// @param radius_TR 右上角半徑
/// @param radius_BL 左下角半徑
/// @param radius_BR 右下角半徑
/// @param rectSize 視圖尺寸
/// @param fillColor 填充色
+ (UIImage *)drawAntiRoundedCornerWithRadius_TL:(float)radius_TL
radius_TR:(float)radius_TR
radius_BL:(float)radius_BL
radius_BR:(float)radius_BR
rectSize:(CGSize)rectSize
fillColor:(UIColor *)fillColor {
UIGraphicsBeginImageContextWithOptions(rectSize, false, [UIScreen mainScreen].scale);
CGContextRef contextRef = UIGraphicsGetCurrentContext();
UIBezierPath *bezierPath = [UIBezierPath bezierPath];
[ZBCornerRadiusTool configPathWithBezierPath:&bezierPath radius_TL:radius_TL radius_TR:radius_TR radius_BL:radius_BL radius_BR:radius_BR rectSize:rectSize fillColor:fillColor];
CGContextDrawPath(contextRef, kCGPathFillStroke);
UIImage *antiRoundedCornerImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return antiRoundedCornerImage;
}
關(guān)鍵方法:
// 對(duì)任意一個(gè)或多個(gè)角設(shè)置圓角
+ (void)configPathWithBezierPath:(UIBezierPath **)bezier Radius:(float)radius rectSize:(CGSize)rectSize cornerStyle:(UIRectCorner)cornerStyle fillColor:(UIColor *)fillColor {
UIBezierPath *bezierPath = *bezier;
BezierPathTool *tool = [[BezierPathTool alloc]initWithRadius:radius rectSize:rectSize fillColor:fillColor];
if (cornerStyle == UIRectCornerAllCorners) {
[tool configAllCornerPoint];
}else {
if (cornerStyle == UIRectCornerTopLeft) {
[tool configTopLeftPoint];
}
if (cornerStyle == UIRectCornerTopRight) {
[tool configTopRightPoint];
}
if (cornerStyle == UIRectCornerBottomLeft) {
[tool configBottomLeftPoint];
}
if (cornerStyle == UIRectCornerBottomRight) {
[tool configBottomRightPoint];
}
}
bezierPath = [tool configCornerBezierPath:bezierPath];
}
// 對(duì)四個(gè)角分別設(shè)置圓角辜贵,0即為不進(jìn)行圓角設(shè)置
+ (void)configPathWithBezierPath:(UIBezierPath **)bezier
radius_TL:(float)radius_TL
radius_TR:(float)radius_TR
radius_BL:(float)radius_BL
radius_BR:(float)radius_BR
rectSize:(CGSize)rectSize
fillColor:(UIColor *)fillColor {
UIBezierPath *bezierPath = *bezier;
BezierPathTool *tool = [[BezierPathTool alloc]initWithRadius_TopLeft:radius_TL radius_TopRight:radius_TR radius_BottomLeft:radius_BL radius_BottomRight:radius_BR rectSize:rectSize fillColor:fillColor];
bezierPath = [tool configCornerBezierPath:bezierPath];
}
最后一步: OK悯蝉,最后一步的實(shí)現(xiàn)都寫在了BezierPathTool
類里,對(duì)應(yīng)兩個(gè)初始化方法托慨,分別有不同的初始化
- (instancetype)initWithRadius:(float)radius rectSize:(CGSize)rectSize fillColor:(UIColor *)fillColor {
if (self = [super init]) {
_radius = radius;
_rectSize = rectSize;
_fillColor = fillColor;
[self configData]; // 默認(rèn)每個(gè)點(diǎn)都是在四個(gè)拐角
}
return self;
}
- (instancetype)initWithRadius_TopLeft:(float)radius_TL
radius_TopRight:(float)radius_TR
radius_BottomLeft:(float)radius_BL
radius_BottomRight:(float)radius_BR
rectSize:(CGSize)rectSize
fillColor:(UIColor *)fillColor {
if (self = [super init]) {
_radius_TopLeft = radius_TL;
_radius_TopRight = radius_TR;
_radius_BottomLeft = radius_BL;
_radius_BottomRight = radius_BR;
_rectSize = rectSize;
_fillColor = fillColor;
[self configCornerPointData];
}
return self;
}
- (void)configData {
_hLeftUpPoint = CGPointMake(0, 0);
_hRightUpPoint = CGPointMake(_rectSize.width, 0);
_hLeftDownPoint = CGPointMake(0, _rectSize.height);
_vLeftUpPoint = CGPointMake(0, 0);
_vRightDownPoint = CGPointMake(_rectSize.width, _rectSize.height);
_centerLeftUp = CGPointMake(0, 0);
_centerRightUp = CGPointMake(_rectSize.width, 0);
_centerLeftDown = CGPointMake(0, _rectSize.height);
_centerRightDown = CGPointMake(_rectSize.width, _rectSize.height);
}
- (void)configCornerPointData {
[self configTopLeftPoint];
[self configTopRightPoint];
[self configBottomLeftPoint];
[self configBottomRightPoint];
}
對(duì)于第一種初始化鼻由,對(duì)于需要標(biāo)記的點(diǎn)的默認(rèn)坐標(biāo)分別對(duì)應(yīng)四個(gè)角,目的是為了方便可以對(duì)任意一個(gè)或多個(gè)角進(jìn)行切割厚棵,如下代碼
//四個(gè)角均切割
- (void)configAllCornerPoint {
_hLeftUpPoint = CGPointMake(_radius, 0);//(10,0)
_hRightUpPoint = CGPointMake(_rectSize.width - _radius, 0);//(30,0)
_hLeftDownPoint = CGPointMake(_radius, _rectSize.height);//(10,40)
_vLeftUpPoint = CGPointMake(0, _radius);//(0,10)
_vRightDownPoint = CGPointMake(_rectSize.width, _rectSize.height - _radius);//(40,30)
_centerLeftUp = CGPointMake(_radius, _radius);//(10,10)
_centerRightUp = CGPointMake(_rectSize.width - _radius, _radius);//(30,10)
_centerLeftDown = CGPointMake(_radius, _rectSize.height - _radius);//(10,30)
_centerRightDown = CGPointMake(_rectSize.width - _radius, _rectSize.height - _radius);//(30,30)
}
//左上角圓角設(shè)置
- (void)configTopLeftPoint {
float radius = _radius_TopLeft ?: _radius;
_hLeftUpPoint = CGPointMake(radius, 0);//(10,0)
_vLeftUpPoint = CGPointMake(0, radius);//(0,10)
_centerLeftUp = CGPointMake(radius, radius);//(10,10)
}
//右上角圓角設(shè)置
- (void)configTopRightPoint {
float radius = _radius_TopRight ?: _radius;
_hRightUpPoint = CGPointMake(_rectSize.width - radius, 0);//(30,0)
_centerRightUp = CGPointMake(_rectSize.width - radius, radius);//(30,10)
}
////左下角圓角設(shè)置
- (void)configBottomLeftPoint {
float radius = _radius_BottomLeft ?: _radius;
_hLeftDownPoint = CGPointMake(radius, _rectSize.height);//(10,40)
_centerLeftDown = CGPointMake(radius, _rectSize.height - radius);//(10,30)
}
//右下角圓角設(shè)置
- (void)configBottomRightPoint {
float radius = _radius_BottomRight ?: _radius;
_vRightDownPoint = CGPointMake(_rectSize.width, _rectSize.height - radius);//(40,30)
_centerRightDown = CGPointMake(_rectSize.width - radius, _rectSize.height - radius);//(30,30)
}
嗯蕉世,最后就是調(diào)用貝塞爾曲線方法進(jìn)行設(shè)置就行,代碼在最上面已經(jīng)給出婆硬,這里就不過(guò)多贅述了狠轻。
下面是分別對(duì)UIImageView
和UIButton
的切割效果圖,也不會(huì)產(chǎn)生離屏渲染
上面描述柿祈,如有不正確之處哈误,希望大家指出,謝謝
問(wèn)題拋出
一個(gè)好的封裝躏嚎,不需要任何依賴或者說(shuō)需要減少對(duì)外界的依賴,那么問(wèn)題就來(lái)了菩貌,上面所封裝的依然依賴于控件的約束或者是frame卢佣。
思考了很久,這個(gè)問(wèn)題筆者依然沒(méi)有很好的解決方案箭阶,如果有好的方法虚茶,希望大家指出,再次感謝