這個(gè)是PYKit
的段代碼,點(diǎn)擊這里查看全部
ios 開(kāi)發(fā) 對(duì)于圓角的裁切,爭(zhēng)議很大,于是做了一個(gè)對(duì)比,對(duì)比過(guò)程下面會(huì)有詳細(xì)的解釋,得出結(jié)論為:相同運(yùn)行環(huán)境下,用shapLayer 裁切與用maskToBounds裁切耗時(shí)比例大概為 **6 : 7**
工具的封裝:
以后你可能這樣切圓角
BaseFilletShadowView *view = [BaseFilletShadowView new];
view
.config
.setUpCutRect(CGRectMake(0, 0, -1, -1))//裁切的位置及范圍 優(yōu)先級(jí)低于 cutRectEdgeWithSelf
.setUpCutRectEdgeWithSelf(UIEdgeInsetsMake(20, 20, 20, 20))//裁切的位置及范圍
.setUpRadius(10)//四個(gè)角切圓的圓形的半徑
.setUpLeftTopAddRadius(10)//左上角追加圓角半徑
.setUpLeftBottomAddRadius(10)//左下角追加圓角半徑
.setUpRightTopAddRadius(10)//右上角追加圓角半徑
.setUpRightBottomAddRadius(10);//右下角追加圓角半徑
- 結(jié)構(gòu):內(nèi)部添加了一個(gè)負(fù)責(zé)陰影的
shadowLayer
與一個(gè)負(fù)責(zé)圓形切圖的containerView
,并且根據(jù)config
屬性統(tǒng)一或分別設(shè)置各個(gè)角的弧度。- 調(diào)用:config添加了鏈?zhǔn)秸{(diào)用方法。view 鏈?zhǔn)秸{(diào)用在這里
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;
/**四個(gè)角的半徑控制接口*/
@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;
/**
設(shè)置陰影必須要設(shè)置這個(gè)layer
要保證這個(gè)layer 在最底層
*/
@property (nonatomic,strong) CALayer *shadowLayer;
/**
在這個(gè)上邊布局,
*/
@property (nonatomic,strong) UIView *containerView;
/**
開(kāi)始切圖
*/
- (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: - 創(chuàng)建切圓路徑
- (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
優(yōu)化實(shí)驗(yàn)
以上是用 instruments
的 time profiler
來(lái) 對(duì)運(yùn)行時(shí)間做的統(tǒng)計(jì)
過(guò)程。
- 自定義兩個(gè)tableView :
PYMaskToBoundsTableView
與PYRoundViewTableView
。- 讓兩個(gè)
tableView
,除去cell
的view
的裁切方式不同 外其他都一樣。- 滾動(dòng)其中一個(gè),另外一個(gè)
tableView
需要聯(lián)動(dòng)。- 觀察
tableView
的cellForRow
方法,比較耗時(shí)。