美文网首页iOS 开发每天分享优质文章
OC_灵活的切圆角工具(对UIView切圆角与阴影添加的优化)

OC_灵活的切圆角工具(对UIView切圆角与阴影添加的优化)

作者: LiYaoPeng | 来源:发表于2018-11-15 16:49 被阅读134次

这个是PYKit的段代码,点击这里查看全部

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);//右下角追加圆角半径 
  1. 结构:内部添加了一个负责阴影的shadowLayer 与一个负责圆形切图的containerView,并且根据config属性统一或分别设置各个角的弧度。
  2. 调用: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

优化实验

以上是用 instrumentstime profiler 来 对运行时间做的统计
过程。

  1. 自定义两个tableView : PYMaskToBoundsTableViewPYRoundViewTableView
  2. 让两个tableView,除去 cellview 的裁切方式不同 外其他都一样。
  3. 滚动其中一个,另外一个tableView 需要联动。
  4. 观察tableViewcellForRow方法,比较耗时。

相关文章

网友评论

    本文标题:OC_灵活的切圆角工具(对UIView切圆角与阴影添加的优化)

    本文链接:https://www.haomeiwen.com/subject/ncpyfqtx.html