ios 开发 对于圆角的裁切,争议很大,于是做了一个对比,对比过程下面会有详细的解释,得出结论为:相同运行环境下,用shapLayer 裁切与用maskToBounds裁切耗时比例大概为 **6 : 7**
工具的封装:
以后你可能这样切圆角
BaseFilletShadowView *view = [BaseFilletShadowView new];
view
.config
.setUpCutRect(CGRectMake(0, 0, -1, -1))//裁切的位置及范围 优先级低于 cutRectEdgeWithSelf
.setUpCutRectEdgeWithSelf(UIEdgeInsetsMake(20, 20, 20, 20))//裁切的位置及范围
.setUpRadius(10)//四个角切圆的圆形的半径
.setUpLeftTopAddRadius(10)//左上角追加圆角半径
.setUpLeftBottomAddRadius(10)//左下角追加圆角半径
.setUpRightTopAddRadius(10)//右上角追加圆角半径
.setUpRightBottomAddRadius(10);//右下角追加圆角半径
- 结构:内部添加了一个负责阴影的
shadowLayer
与一个负责圆形切图的containerView
,并且根据config
属性统一或分别设置各个角的弧度。- 调用:config添加了链式调用方法。view 链式调用在这里
config .h
#import <UIKit/UIKit.h>
@interface BaseFilletShadowViewConfig : NSObject
/**裁切的位置及范围*/
@property (nonatomic,assign) CGRect cutRect;
- (BaseFilletShadowViewConfig *(^)(CGRect cutRect)) setUpCutRect;
/**圆形的半径*/
@property (nonatomic,assign) CGFloat radius;
- (BaseFilletShadowViewConfig *(^)(CGFloat radius)) setUpRadius;
/**四个角的半径控制接口*/
@property (nonatomic,assign) CGFloat leftTopAddRadius;
- (BaseFilletShadowViewConfig *(^)(CGFloat leftTopAddRadius)) setUpLeftTopAddRadius;
@property (nonatomic,assign) CGFloat leftBottomAddRadius;
- (BaseFilletShadowViewConfig *(^)(CGFloat leftBottomAddRadius)) setUpLeftBottomAddRadius;
@property (nonatomic,assign) CGFloat rightTopAddRadius;
- (BaseFilletShadowViewConfig *(^)(CGFloat rightTopAddRadius)) setUpRightTopAddRadius;
@property (nonatomic,assign) CGFloat rightBottomAddRadius;
- (BaseFilletShadowViewConfig *(^)(CGFloat rightBottomAddRadius)) setUpRightBottonAddRadius;
///**图片的透明度*/
//@property (nonatomic,assign) CGFloat alpha;
//- (BaseFilletShadowViewConfig *(^)(CGFloat alpha)) setUpAlpha;
@end
config .m
#import "BaseFilletShadowViewConfig.h"
@implementation BaseFilletShadowViewConfig
- (BaseFilletShadowViewConfig *(^)(CGRect cutRect)) setUpCutRect {
return ^(CGRect cutRect) {
self.cutRect = cutRect;
return self;
};
}
- (BaseFilletShadowViewConfig *(^)(CGFloat radius)) setUpRadius {
return ^(CGFloat radius) {
self.radius = radius;
return self;
};
}
- (BaseFilletShadowViewConfig *(^)(CGFloat leftTopRadius)) setUpLeftTopAddRadius {
return ^(CGFloat radius) {
self.leftTopAddRadius = radius;
return self;
};
}
- (BaseFilletShadowViewConfig *(^)(CGFloat leftBottomRadius)) setUpLeftBottomAddRadius {
return ^(CGFloat radius) {
self.leftBottomAddRadius = radius;
return self;
};
}
- (BaseFilletShadowViewConfig *(^)(CGFloat rightTopRadius)) setUpRightTopAddRadius {
return ^(CGFloat radius) {
self.rightTopAddRadius = radius;
return self;
};
}
- (BaseFilletShadowViewConfig *(^)(CGFloat rightBottomRadius)) setUpRightBottonAddRadius {
return ^(CGFloat radius) {
self.rightBottomAddRadius = radius;
return self;
};
}
- (BaseFilletShadowViewConfig *(^)(CGFloat alpha)) setUpAlpha {
return ^(CGFloat alpha) {
self.alpha = alpha;
return self;
};
}
@end
BaseFilletShadowView.h
#import <UIKit/UIKit.h>
#import "BaseFilletShadowViewConfig.h"
/// 圆角阴影view
@interface BaseFilletShadowView : UIView
@property (nonatomic,strong) BaseFilletShadowViewConfig *config;
/**
设置阴影必须要设置这个layer
要保证这个layer 在最底层
*/
@property (nonatomic,strong) CALayer *shadowLayer;
/**
在这个上边布局,
*/
@property (nonatomic,strong) UIView *containerView;
/**
开始切图
*/
- (void) reCut;
/**
取消切图
*/
- (void) unCunt;
@end
BaseFilletShadowView.m
#import "BaseFilletShadowView.h"
#import <Masonry/Masonry.h>
@interface BaseFilletShadowView ()
@property (nonatomic,strong) CAShapeLayer *shapeLayer;
@property (nonatomic,assign) BOOL isCut;
@property (nonatomic,assign) CGRect lastDrawFrame;
@end
@implementation BaseFilletShadowView
#pragma mark - init
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self.layer addSublayer:self.shadowLayer];
[self addSubview:self.containerView];
[self.containerView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self);
}];
}
return self;
}
#pragma mark - functions
- (void) unCunt {
self.containerView.layer.mask = nil;
}
- (void) reCut {
self.isCut = true;
if (self.config.radius <= 0
&& self.config.leftTopAddRadius <= 0
&& self.config.leftBottomAddRadius <= 0
&& self.config.rightTopAddRadius <= 0
&& self.config.rightBottomAddRadius <= 0) {
return;
}
self.containerView.layer.mask = self.shapeLayer;
[self setupShapeLayerIfNeede];
}
- (void) setupShapeLayerIfNeede {
if (CGSizeEqualToSize(self.frame.size, self.lastDrawFrame.size)) {
return;
}
self.lastDrawFrame = self.frame;
__weak typeof(self)weakSelf = self;
[self createPathWithRect:self.config.cutRect andCallBack:^(CGMutablePathRef path) {
weakSelf.shapeLayer.path = path;
weakSelf.shadowLayer.shadowPath = path;
}];
[self setupShadowLayer];
}
- (void) setupShadowLayer {
self.shadowLayer.backgroundColor = self.backgroundColor.CGColor;
self.shadowLayer.frame = self.bounds;
}
//MARK: - 创建切圆路径
- (void) createPathWithRect:(CGRect)rect andCallBack: (void(^)(CGMutablePathRef path))block {
if (!block) return;
CGMutablePathRef path = [self createMutablePathRefWithRect:rect];
block(path);
CFRelease(path);
}
- (CGMutablePathRef)createMutablePathRefWithRect:(CGRect)rect {
CGRect cutRect = rect;
CGFloat
minx = CGRectGetMinX(cutRect),//矩形中最小的x
midx = CGRectGetMidX(cutRect),//矩形中最大x值的一半
maxx = CGRectGetMaxX(cutRect);//矩形中最大的x值
CGFloat
miny = CGRectGetMinY(cutRect),//矩形中最小的Y值
midy = CGRectGetMidY(cutRect),//矩形中最大Y值的一半
maxy = CGRectGetMaxY(cutRect);//矩形中最大的Y值
CGFloat
radius = self.config.radius;
CGFloat
leftTopRadiu = radius + self.config.leftTopAddRadius,
rightTopRadiu = radius + self.config.rightTopAddRadius,
leftBottomRadiu = radius + self.config.leftBottomAddRadius,
rightBottomRadiu = radius + self.config.rightBottomAddRadius;
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, nil, minx, midy);
CGPathAddArcToPoint(path, nil, minx, miny, midx, miny, leftTopRadiu);
CGPathAddArcToPoint(path, nil, maxx, miny, maxx, midy, rightTopRadiu);
CGPathAddArcToPoint(path, nil, maxx, maxy, midx, maxy, rightBottomRadiu);
CGPathAddArcToPoint(path, nil, minx, maxy, minx, midy, leftBottomRadiu);
return path;
}
// MARK: properties get && set
- (BaseFilletShadowViewConfig *)config {
if (!_config) {
_config = [BaseFilletShadowViewConfig new];
_config
.setUpCutRect(self.bounds)
.setUpRadius(0)
.setUpAlpha(1);
}
return _config;
}
- (CAShapeLayer *)shapeLayer {
if(!_shapeLayer) {
_shapeLayer = [CAShapeLayer new];
}
return _shapeLayer;
}
- (UIView *)containerView {
if (!_containerView) {
_containerView = [UIView new];
}
return _containerView;
}
- (CALayer *)shadowLayer {
if (!_shadowLayer) {
_shadowLayer = [CALayer new];
}
return _shadowLayer;
}
- (void)layoutSubviews {
[super layoutSubviews];
self.shapeLayer.frame = self.bounds;
self.shadowLayer.frame = self.bounds;
}
@end
优化实验
以上是用 instruments
的 time profiler
来 对运行时间做的统计
过程。
- 自定义两个tableView :
PYMaskToBoundsTableView
与PYRoundViewTableView
。- 让两个
tableView
,除去cell
的view
的裁切方式不同 外其他都一样。- 滚动其中一个,另外一个
tableView
需要联动。- 观察
tableView
的cellForRow
方法,比较耗时。