对于目前手机的性能来说,对于少量圆角设置带来的离屏渲染问题,带来的卡顿问题已经并不是那么的明显了,但是对于一些类型的app来说,项目中会有大量的圆角设置,同时又对性能要求比较高的话,那么避免圆角设置带来的性能问题,还是有必要解决的。
iOS设置圆角方法以及指定位置设圆角
iOS圆角设置的几种方法的对比以及性能检测
上面两篇文章中介绍了iOS常见的设置圆角的方法,以及对应方法性能的检测😊。
然而在实际项目中仍然有其他问题存在,也就是前面的总结仍有不足,下面提出两个遇到的问题
针对Masonry
布局的控件,如何进行圆角设置?
其实解决方案非常简单,调用layoutIfNeeded
函数进行布局,然后所约束的控件才会按照约束条件,生成当前布局相应的frame
和bounds
,那么就可以进行各种样式的圆角设置
针对四个不同的角,设置不同的半径,如何操作?
方法
实际上与之前文章里所描述的一种方法混合图层一个道理。在所需切圆角的控件上加一层,利用贝塞尔切割出不同角所需要的圆角半径
思路
思路如上图所示,标记了几个点,进行画圆弧以及连线,具体代码如下
[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];
上面的代码具体是切割圆角,具体圆角半径与对应点的位置有关,合理的利用上图所描述的点的坐标,可以切割出不同的弧度,同时利用位移枚举UIRectCorner
可实现任意一个或多个角的切割。
具体代码
第一步: 新增了一个UIVIew+CornerRadius
的分类,.h
文件里声明了两种切割圆角的方法。
@interface UIView (CornerRadius)
/// 绘制裁剪圆角,可选任意角进行切割
/// @param radius 圆角半径
/// @param fillColor 填充色
/// @param type 裁剪角
- (void)drawCircularBeadImageWithRadius:(float)radius fillColor:(UIColor *)fillColor CornerStyle:(UIRectCorner)type;
/// 绘制裁剪圆角,可选任意角进行切割,且每个角可选切割任意半径
/// @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
具体的实现:
@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
从上面代码可以看出,在这里我们在需要切割圆角的控件上,加了一层UIImageView
,同时这里利用Masonry
对新增的图层进行了约束,最重要的一句代码[self layoutIfNeeded];
千万不能少,这句话,就解决了上面所说的第一个问题。
这里代码均是针对利用Masonry
布局约束的控件去实现圆角,如果是利用frame
的也非常方面,直接利用控件的size
便可。笔者只提供一下思路
第二步: 添加两个工具类,先看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;
}
关键方法:
// 对任意一个或多个角设置圆角
+ (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];
}
// 对四个角分别设置圆角,0即为不进行圆角设置
+ (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,最后一步的实现都写在了BezierPathTool
类里,对应两个初始化方法,分别有不同的初始化
- (instancetype)initWithRadius:(float)radius rectSize:(CGSize)rectSize fillColor:(UIColor *)fillColor {
if (self = [super init]) {
_radius = radius;
_rectSize = rectSize;
_fillColor = fillColor;
[self configData]; // 默认每个点都是在四个拐角
}
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];
}
对于第一种初始化,对于需要标记的点的默认坐标分别对应四个角,目的是为了方便可以对任意一个或多个角进行切割,如下代码
//四个角均切割
- (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)
}
//左上角圆角设置
- (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)
}
//右上角圆角设置
- (void)configTopRightPoint {
float radius = _radius_TopRight ?: _radius;
_hRightUpPoint = CGPointMake(_rectSize.width - radius, 0);//(30,0)
_centerRightUp = CGPointMake(_rectSize.width - radius, radius);//(30,10)
}
////左下角圆角设置
- (void)configBottomLeftPoint {
float radius = _radius_BottomLeft ?: _radius;
_hLeftDownPoint = CGPointMake(radius, _rectSize.height);//(10,40)
_centerLeftDown = CGPointMake(radius, _rectSize.height - radius);//(10,30)
}
//右下角圆角设置
- (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)
}
嗯,最后就是调用贝塞尔曲线方法进行设置就行,代码在最上面已经给出,这里就不过多赘述了。
下面是分别对UIImageView
和UIButton
的切割效果图,也不会产生离屏渲染
上面描述,如有不正确之处,希望大家指出,谢谢
问题抛出
一个好的封装,不需要任何依赖或者说需要减少对外界的依赖,那么问题就来了,上面所封装的依然依赖于控件的约束或者是frame。
思考了很久,这个问题笔者依然没有很好的解决方案,如果有好的方法,希望大家指出,再次感谢
网友评论