图像Core Animation

UIBezierPath与CAShapeLayer学习笔记

2020-01-22  本文已影响0人  寻心_0a46

UIBezierPath

UIBezierPath是UIKit 中的一个类,继承自NSObject,可以创建基于矢量的路径。此类是Core Graphics框架关于path的一个OC封装。此类仅指定路径的几何图形。路径可以定义简单的形状,如矩形、椭圆和圆弧,也可以定义包含直线段和曲线段的复杂多边形。每一个直线段或者曲线段的结束的地方是下一个的开始的地方。每一个连接的直线或者曲线段的集合成为subpath。一个UIBezierPath对象定义一个完整的路径包括一个或者多个subpaths。

CAShapeLayer

CAShapeLayer继承自CALayer。CAShapeLayer属于CoreAnimation框架,通过GPU来渲染图形,节省性能。动画渲染直接提交给手机GPU,不消耗内存。 每个CAShapeLayer对象都代表着将要被渲染到屏幕上的一个任意的形状(shape)。具体的形状由其path(类型为CGPathRef)属性指定。 普通的CALayer是矩形,需要frame属性。CAShapeLayer初始化时也需要指定frame值,但它本身没有形状,它的形状来源于其属性path 。CAShapeLayer中shape需要形状才能生效。UIBezierPath可以为其提供绘制形状的path。

UIBezierPath常用属性

@property(nonatomic) CGPathRef CGPath;

属性描述 : 核心图形的路径表示。此属性包含任意给定时间点的路径快照。获取此属性将返回一个不可变的path对象,您可以将该对象传递给核心图形函数。路径对象本身是由UIBezierPath对象拥有的,它只有在你对路径做进一步修改时才有效。可以将此属性的值设置为使用Core Graphics框架的函数构建的路径。在设置新路径时,此方法将复制您提供的路径。

@property(nonatomic) CGPathRef CGPath;

例如 :

pathAnimation.fromValue = (__bridge id _Nullable)(beginPath.CGPath);
pathAnimation.toValue = (__bridge id _Nullable)(endPath.CGPath);

UIBezierPath常用函数

+ (instancetype)bezierPath;

函数描述 : 创建并返回一个新的UIBezierPath对象。

返回值 : 一个新的空路径对象。

+ (instancetype)bezierPath;

+ (instancetype)bezierPathWithRect:(CGRect)rect;

属性描述 : 创建并返回一个用矩形路径初始化的新UIBezierPath对象。此方法通过从rect的原点开始并按顺时针方向(相对于默认坐标系统)添加线段来创建闭合的子路径。

参数 :

rect : 描述要创建的路径的矩形。

返回值 :一个带有矩形路径的新路径对象。

+ (instancetype)bezierPathWithRect:(CGRect)rect;

+ (instancetype)bezierPathWithOvalInRect:(CGRect)rect;

函数描述 : 创建并返回一个新的UIBezierPath对象,该对象初始化时使用指定矩形内接的椭圆路径。该方法使用贝塞尔曲线序列创建一个接近椭圆的闭合子路径。路径是按顺时针方向创建的(相对于默认坐标系统)。如果rect参数指定一个正方形,则内接路径为圆。

参数 :

rect : 画椭圆的长方形。

返回值 : 一个带有椭圆路径的新路径对象。

+ (instancetype)bezierPathWithOvalInRect:(CGRect)rect;

+ (instancetype)bezierPathWithRoundedRect:(CGRect)rect byRoundingCorners:(UIRectCorner)corners cornerRadii:(CGSize)cornerRadii;

函数描述 : 创建并返回一个新的UIBezierPath对象,该对象初始化为一个圆角矩形路径。此方法创建一个闭合的子路径,在创建必要的线段和曲线段时,按顺时针方向(相对于默认坐标系统)进行。

参数 :

rect : 定义路径的基本形状的矩形。

corners : 位掩码值,用于标识要舍入的角。可以使用此参数只对矩形的一部分角进行舍入。

cornerRadii : 每个角的半径为椭圆形。大于矩形宽度或高度一半的值被适当地钳制为宽度或高度的一半。

返回值 : 一个带有圆形矩形路径的新路径对象。

+ (instancetype)bezierPathWithRoundedRect:(CGRect)rect byRoundingCorners:(UIRectCorner)corners cornerRadii:(CGSize)cornerRadii;

+ (instancetype)bezierPathWithArcCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise;

函数描述 : 创建并返回一个新的用圆弧初始化的UIBezierPath对象。此方法创建打开的子路径。创建的圆弧位于指定圆的周长上。例如,在默认坐标系中绘制时,指定0弧度的开始角,π弧度的结束角,并将顺时针参数设置为“是”将绘制圆的下半部分。但是,指定相同的开始和结束角度,但将顺时针参数设置为“否”将绘制圆的上半部分。调用此方法后,将当前点设置为圆弧上圆的结束角处的点。

参数 :

center : 指定用于定义圆弧的圆的圆心(在当前坐标系中)。

radius : 指定用于定义圆弧的圆的半径。

startAngle : 指定圆弧的起始角度(以弧度度量)。

endAngle :指定圆弧的结束角度(以弧度度量)。

clockwise : 画弧的方向。

返回值 :具有指定弧的新路径对象。

+ (instancetype)bezierPathWithArcCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise;

- (void)moveToPoint:(CGPoint)point;

函数描述 : 将接收器的当前点移动到指定位置。此方法隐式地结束当前子路径(如果有),并将当前点设置为point参数中的值。在结束前一个子路径时,此方法实际上并不关闭子路径。因此,前一个子路径的第一个和最后一个点彼此不连接。对于许多路径操作,您必须在发出导致绘制直线或曲线段的任何命令之前调用此方法。

参数 :

point : 在当前坐标系中的一个点。

- (void)moveToPoint:(CGPoint)point;

- (void)addLineToPoint:(CGPoint)point;

函数描述 : 向接收器的路径追加一条直线。此方法创建一个从当前点开始并在point参数指定的点结束的直线段。在添加线段之后,此方法将当前点更新为in point值。在调用此方法之前,必须设置路径的当前点(使用moveToPoint:方法或通过先前创建的直线或曲线段)。如果路径为空,则此方法不执行任何操作。

参数 :

point : 在当前坐标系中指定的线段的终点。

- (void)addLineToPoint:(CGPoint)point;

- (void)addArcWithCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise API_AVAILABLE(ios(4.0));

函数描述 :在接收器的路径上附加一个弧。此方法添加从当前点开始的指定弧。创建的弧位于指定圆的周长上。例如,指定一个角0开始弧度,π结束角弧度,将顺时针参数设置为YES画圆的下半部分。但是,如果指定相同的开始和结束角,而将顺时针参数设置为NO,则会绘制圆的上半部分。调用此方法后,将当前点设置为圆弧上圆角端点处的点。

参数 :

center : 指定用于定义圆弧的圆(在当前坐标系中)的中心点。

radius : 指定用于定义弧的圆的半径。

startAngle :指定弧的起始角(以弧度度量)。

endAngle :指定弧的结束角(以弧度度量)。

clockwise : 画弧的方向。

- (void)addArcWithCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise API_AVAILABLE(ios(4.0));

- (void)closePath;

函数描述 : 关闭最近添加的子路径。此方法通过在子路径的第一个点和最后一个点之间创建线段来关闭当前子路径。此方法随后将当前点更新到新创建的线段的末尾,这也是现在关闭的子路径中的第一个点。

- (void)closePath;

- (void)removeAllPoints;

函数描述 : 从接收器中删除所有点,从而有效地删除所有子路径。

- (void)removeAllPoints;

- (void)appendPath:(UIBezierPath *)bezierPath;

函数描述 : 将指定路径对象的内容附加到接收器的路径。此方法将用于在bezierPath中创建路径的命令添加到接收方路径的末端。此方法不会显式地尝试连接两个对象中的子路径,尽管bezierPath中的操作仍然可能导致这种效果。

参数 :

bezierPath : 要添加到接收器的路径。

- (void)appendPath:(UIBezierPath *)bezierPath;

CAShapeLayer常用属性

@property(nullable) CGPathRef path;

属性描述 : 定义要渲染的形状的路径。如果路径延伸到层边界之外,只有在正常层遮罩规则导致该情况的情况下,它才会自动裁剪到该图层。分配后,将复制路径。默认为空。可设置动画。(请注意,尽管“路径”属性是可设置动画的,但更改该属性时不会创建隐式动画。)

@property(nullable) CGPathRef path;

@property(copy) CAShapeLayerLineCap lineCap;

属性描述 : 指定形状路径的线帽样式。线帽样式指定笔划时开放路径端点的形状。lineCap中描述了支持的值,默认值为kCALineCapButt。

@property(copy) CAShapeLayerLineCap lineCap;
/* `lineCap' values. */
//指定笔划时开放路径的端点的对接线封口样式。
CA_EXTERN CAShapeLayerLineCap const kCALineCapButt
    API_AVAILABLE(macos(10.6), ios(3.0), watchos(2.0), tvos(9.0));
//指定笔划时开放路径的端点的圆形线帽样式。
CA_EXTERN CAShapeLayerLineCap const kCALineCapRound
    API_AVAILABLE(macos(10.6), ios(3.0), watchos(2.0), tvos(9.0));
//指定笔划时开放路径端点的方形线帽样式。
CA_EXTERN CAShapeLayerLineCap const kCALineCapSquare
    API_AVAILABLE(macos(10.6), ios(3.0), watchos(2.0), tvos(9.0));

@property(nullable, copy) NSArray<NSNumber *> *lineDashPattern;

属性描述 :划线时应用于形状路径的虚线图案。虚线图案指定为NSNumber对象数组,这些对象分别指定虚线图案的已绘制线段和未绘制线段的长度。例如,传递一个值为[2,3]的数组会设置一个dash模式,该模式在2个用户空间的长绘制段和3个用户空间的长未绘制段之间交替。传递值[10,5,5,5]将模式设置为一个10单元的已绘制段、一个5单元的未绘制段、一个5单元的已绘制段和一个5单元的未绘制段。默认为nil,实线。

@property(nullable, copy) NSArray<NSNumber *> *lineDashPattern;

@property CGFloat strokeStart;

属性描述 : 停止描绘路径的相对位置,可设置动画。此属性的值必须在0.0到1.0之间。此属性的默认值为0.0。结合strokeEnd属性,此属性定义要笔划的路径的子区域。此属性中的值指示开始笔划的路径上的相对点,而strokeEnd属性定义结束点。值0.0表示路径的开始,值1.0表示路径的结束。中间的值沿路径长度线性解释。

@property CGFloat strokeStart;

@property CGFloat strokeEnd;

属性描述 : 停止描绘路径的相对位置,可设置动画。此属性的值必须在0.0到1.0之间。此属性的默认值为1.0。与strokeStart属性相结合,该属性定义了笔画路径的子区域。此属性中的值指示完成描绘的路径上的相对点,而strokeStart属性则定义起点。0.0表示路径的开始,1.0表示路径的结束。中间的值是沿着路径长度线性解释的。

@property CGFloat strokeEnd;

@property CGFloat lineWidth;

属性描述 : 指定形状路径的线宽。可设置动画。

@property CGFloat lineWidth;

@property CGRect frame;

属性描述 : 层的矩形frame(框架)。frame是在superlayer’s的坐标空间中指定的层的位置和大小,对于层,frame矩形是一个计算属性,它是从“边界”、“锚点”和“位置”属性中的值派生出来的。为该属性指定新值时,图层将更改其“位置和边界”属性以匹配指定的矩形。矩形中每个坐标的值都是以点为单位测量的。

如果transform属性应用的旋转变换不是90度的倍数,则不要设置框架。

frame属性不是直接可动画的。相反,应该激活边界、锚点和位置属性的适当组合,以实现所需的结果。

@property CGRect frame;

@property(nullable) CGColorRef fillColor;

属性描述 : 用于填充形状路径的颜色。可设置动画。将fillColor设置为nil将不会渲染填充。默认为不透明黑色。

@property(nullable) CGColorRef fillColor;

@property(nullable) CGColorRef strokeColor;

属性描述 : 用于绘制形状路径的颜色。可设置动画。将strokeColor设置为nil将不会呈现笔划。默认是nil。

@property(nullable) CGColorRef strokeColor;

@property(copy) CAShapeLayerFillRule fillRule;

属性描述 : 填充形状路径时使用的填充规则。可能的值显示在形状填充模式值中。默认值为kCAFillRuleNonZero。

@property(copy) CAShapeLayerFillRule fillRule;

测试代码

比例视图动画:

//
//  CircleScaleView.h

#import <UIKit/UIKit.h>



@interface CircleScaleView : UIView

@property (nonatomic,assign)float maxValue;//最大值
@property (nonatomic,assign)float currentValue;//当前值

@end

//
//  CircleScaleView.m

#import "CircleScaleView.h"

@interface CircleScaleView()<CAAnimationDelegate>

@end

@implementation CircleScaleView

- (void)drawRect:(CGRect)rect {
    //背景圆环的贝塞尔曲线
    UIBezierPath *backgroundPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(self.bounds.size.width / 2,self.bounds.size.height / 2) radius:self.bounds.size.height / 2 - 9 / 2 startAngle:(3*M_PI) / 4  endAngle:M_PI / 4 clockwise:YES];
    UIColor *storkeColor = [UIColor grayColor];
    [storkeColor setStroke];
    backgroundPath.lineWidth = 5;
    [backgroundPath stroke];

    //设置比例动画
    [self setScaleAnimation];
    //设置比例标签
    [self setScaleLabel];
}

- (void)setScaleAnimation{
    //所占比例的贝塞尔曲线
    UIBezierPath *scalePath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(self.bounds.size.width / 2,self.bounds.size.height / 2) radius:self.bounds.size.height / 2 - 9 / 2 startAngle:(3*M_PI) / 4 endAngle:((3*M_PI) / 4 + self.currentValue/self.maxValue * 2 * M_PI) clockwise:YES];
    
    //设置形状,渲染图形
    CAShapeLayer *circleLayer = [CAShapeLayer layer];
    circleLayer.path = scalePath.CGPath;
    circleLayer.lineCap = kCALineCapRound;
    circleLayer.strokeEnd = 0.75;
    circleLayer.lineWidth = 5;
    circleLayer.frame = self.bounds;
    circleLayer.fillColor = [UIColor clearColor].CGColor;
    circleLayer.strokeColor = [UIColor greenColor].CGColor;
    [self.layer addSublayer:circleLayer];
    
    //设置动画
    CABasicAnimation *baseAnima = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
    baseAnima.duration = 1.5;
    baseAnima.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
    baseAnima.delegate = self;
    baseAnima.fromValue = [NSNumber numberWithInteger:0];
    [circleLayer addAnimation:baseAnima forKey:@"strokeEndAnimation"];
}

- (void)setScaleLabel{
    //设置比例标签
    UILabel *scaleLabel = [[UILabel alloc] initWithFrame:CGRectZero];
    scaleLabel.text = [NSString stringWithFormat:@"已领\r%d%%",(int)floor(self.currentValue / self.maxValue * 100)];
    scaleLabel.textColor = [UIColor greenColor];;
    scaleLabel.textAlignment = NSTextAlignmentCenter;
    scaleLabel.font = [UIFont systemFontOfSize:12];
    scaleLabel.numberOfLines = 0;
    [self addSubview:scaleLabel];
    [scaleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
        make.center.equalTo(self);
    }];
}

@end

//
//  BezierPathController.h


#import <UIKit/UIKit.h>


@interface BezierPathController : UIViewController

@end

//
//  BezierPathController.m


#import "BezierPathController.h"
#import "CircleScaleView.h"

@interface BezierPathController ()

@end

@implementation BezierPathController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    CircleScaleView *circleView = [[CircleScaleView alloc] initWithFrame:CGRectZero];
    circleView.backgroundColor = [UIColor whiteColor];
    circleView.maxValue = 10;
    circleView.currentValue = 5;
    [self.view addSubview:circleView];
    [circleView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.center.equalTo(self.view);
        make.size.mas_equalTo(CGSizeMake(100, 100));
    }];
    
}

@end

效果如图 :

Jietu20200118-232121.gif

贝塞尔曲线画圆具参考价值的图片(红色标注只代表大体所在位置,不代表精度):

circular.png

例如添加圆角 :

//
//  CircleScaleView.m

#import "CircleScaleView.h"

@interface CircleScaleView()<CAAnimationDelegate>

@end

@implementation CircleScaleView

- (void)drawRect:(CGRect)rect {
    
    UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:rect byRoundingCorners:UIRectCornerTopLeft | UIRectCornerTopRight cornerRadii:CGSizeMake(30, 30)];
    CAShapeLayer *shapLayer = [CAShapeLayer layer];
    shapLayer.frame = self.bounds;
    shapLayer.path = maskPath.CGPath;
    self.layer.mask = shapLayer;
}

@end

效果如图:

截屏2020-01-23下午1.08.45.png
//
//  BezierPathController.m

#import "BezierPathController.h"
#import "CircleScaleView.h"

@interface BezierPathController ()

@end

@implementation BezierPathController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    CircleScaleView *circleView = [[CircleScaleView alloc] initWithFrame:CGRectZero];
    circleView.backgroundColor = [UIColor redColor];
    circleView.maxValue = 10;
    circleView.currentValue = 5;
    [self.view addSubview:circleView];
    [circleView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.center.equalTo(self.view);
        make.size.mas_equalTo(CGSizeMake(300, 300));
    }];
    
}

@end

例如为带边框的标签添加三个圆角:

//
//  BezierPathController.m


#import "BezierPathController.h"
#import "CircleScaleView.h"

@interface BezierPathController ()

@end

@implementation BezierPathController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.navigationItem.title = @"贝塞尔曲线图";
    
    CircleScaleView *circleView = [[CircleScaleView alloc] initWithFrame:CGRectZero];
    circleView.backgroundColor = [UIColor redColor];
    circleView.maxValue = 10;
    circleView.currentValue = 5;
    [self.view addSubview:circleView];
    [circleView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.center.equalTo(self.view);
        make.size.mas_equalTo(CGSizeMake(300, 300));
    }];
    
    UILabel *testLabel = [[UILabel alloc]initWithFrame:CGRectZero];
    testLabel.text = @"贝塞尔曲线图";
    testLabel.font = [UIFont systemFontOfSize:15];
    testLabel.textColor = [UIColor redColor];
    testLabel.textAlignment = NSTextAlignmentCenter;
    [self.view addSubview:testLabel];
    [testLabel mas_makeConstraints:^(MASConstraintMaker *make) {
        make.bottom.equalTo(circleView.mas_top).offset(- 60);
        make.width.mas_equalTo([self calculateTheSizeOfTheText:@"贝塞尔曲线图"].width + 20);
        make.height.mas_equalTo([self calculateTheSizeOfTheText:@"贝塞尔曲线图"].height + 20);
        make.centerX.equalTo(self.view);
    }];
    
    [self.view layoutIfNeeded];
    [self drawOuterBorder:testLabel];
    
}

///返回单个字符的高度
- (CGSize)singleCharactorSizeWithFont:(UIFont *)font {
    NSString *text = @"C";
    return [text sizeWithAttributes:@{NSFontAttributeName: font}];
}

///计算文本的的大小
- (CGSize)calculateTheSizeOfTheText:(NSString *)text{
    CGSize itemSize = CGSizeZero;
    itemSize.height = [self singleCharactorSizeWithFont:[UIFont systemFontOfSize:15]].height;
    CGSize size = CGSizeMake (CGFLOAT_MAX,itemSize.height);
    CGRect rect = [text boundingRectWithSize:size options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingTruncatesLastVisibleLine attributes:@{NSFontAttributeName : [UIFont systemFontOfSize:15]} context:nil];
    itemSize = rect.size;
    return itemSize;
}

///为视图添加指定位置圆角
- (void)viewAddCornerRadius:(UIView *)view applyRoundCorners:(UIRectCorner)corners radius:(CGFloat)radius{
    UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:view.bounds byRoundingCorners:corners cornerRadii:CGSizeMake(radius, radius)];
    CAShapeLayer *maskLayer = [CAShapeLayer layer];
    maskLayer.frame = view.bounds;
    maskLayer.path = maskPath.CGPath;
    view.layer.mask = maskLayer;
}


///绘制外边框
- (void)drawOuterBorder:(UIView *)view{
    view.layer.borderColor = [UIColor greenColor].CGColor;
    view.layer.borderWidth = 2.0;
    [self viewAddCornerRadius:view applyRoundCorners:UIRectCornerTopLeft | UIRectCornerTopRight | UIRectCornerBottomRight radius:([self calculateTheSizeOfTheText:@"贝塞尔曲线图"].height + 20) / 2];
}

效果如图,并不理想:

截屏2020-02-27下午11.17.41.png

可以改为不直接在标签上设置边框,而是由CAShapeLayer绘制出来,例如:

//
//  BezierPathController.m


#import "BezierPathController.h"
#import "CircleScaleView.h"

@interface BezierPathController ()

@end

@implementation BezierPathController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.navigationItem.title = @"贝塞尔曲线图";
    
    CircleScaleView *circleView = [[CircleScaleView alloc] initWithFrame:CGRectZero];
    circleView.backgroundColor = [UIColor redColor];
    circleView.maxValue = 10;
    circleView.currentValue = 5;
    [self.view addSubview:circleView];
    [circleView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.center.equalTo(self.view);
        make.size.mas_equalTo(CGSizeMake(300, 300));
    }];
    
    UILabel *testLabel = [[UILabel alloc]initWithFrame:CGRectZero];
    testLabel.text = @"贝塞尔曲线图";
    testLabel.font = [UIFont systemFontOfSize:15];
    testLabel.textColor = [UIColor redColor];
    testLabel.textAlignment = NSTextAlignmentCenter;
    [self.view addSubview:testLabel];
    [testLabel mas_makeConstraints:^(MASConstraintMaker *make) {
        make.bottom.equalTo(circleView.mas_top).offset(- 60);
        make.width.mas_equalTo([self calculateTheSizeOfTheText:@"贝塞尔曲线图"].width + 20);
        make.height.mas_equalTo([self calculateTheSizeOfTheText:@"贝塞尔曲线图"].height + 20);
        make.centerX.equalTo(self.view);
    }];
    
    [self.view layoutIfNeeded];
    [self drawOuterBorder:testLabel];
    
}

///返回单个字符的高度
- (CGSize)singleCharactorSizeWithFont:(UIFont *)font {
    NSString *text = @"C";
    return [text sizeWithAttributes:@{NSFontAttributeName: font}];
}

///计算文本的的大小
- (CGSize)calculateTheSizeOfTheText:(NSString *)text{
    CGSize itemSize = CGSizeZero;
    itemSize.height = [self singleCharactorSizeWithFont:[UIFont systemFontOfSize:15]].height;
    CGSize size = CGSizeMake (CGFLOAT_MAX,itemSize.height);
    CGRect rect = [text boundingRectWithSize:size options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingTruncatesLastVisibleLine attributes:@{NSFontAttributeName : [UIFont systemFontOfSize:15]} context:nil];
    itemSize = rect.size;
    return itemSize;
}

///为视图添加指定位置圆角
- (void)viewAddCornerRadius:(UIView *)view applyRoundCorners:(UIRectCorner)corners radius:(CGFloat)radius{
    UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:view.bounds byRoundingCorners:corners cornerRadii:CGSizeMake(radius, radius)];
    CAShapeLayer *maskLayer = [CAShapeLayer layer];
    maskLayer.frame = view.bounds;
    maskLayer.path = maskPath.CGPath;
    view.layer.mask = maskLayer;
}

///为视图添加指定位置的圆角
- (void)viewAddCornerRadius2:(UIView *)view applyRoundCorners:(UIRectCorner)corners radius:(CGFloat)radius{
    UIBezierPath *outerBorderPath = [UIBezierPath bezierPathWithRoundedRect:view.bounds byRoundingCorners:UIRectCornerTopLeft | UIRectCornerTopRight | UIRectCornerBottomRight cornerRadii:CGSizeMake(radius, radius)];
    CAShapeLayer *borderLayer = [CAShapeLayer layer];
    borderLayer.path = outerBorderPath.CGPath;
    borderLayer.lineWidth = 2.0;
    borderLayer.frame = view.bounds;
    borderLayer.fillColor = [UIColor clearColor].CGColor;
    borderLayer.strokeColor = [UIColor greenColor].CGColor;
    [view.layer addSublayer:borderLayer];
}

///绘制外边框
- (void)drawOuterBorder:(UIView *)view{
    [self viewAddCornerRadius2:view applyRoundCorners:UIRectCornerTopLeft | UIRectCornerTopRight | UIRectCornerBottomRight radius:([self calculateTheSizeOfTheText:@"贝塞尔曲线图"].height + 20) / 2];
}

效果如图 :

截屏2020-02-27下午11.44.14.png

例如为视图添加虚线边框

//
//  BezierPathController.m


#import "BezierPathController.h"
#import "CircleScaleView.h"

@interface BezierPathController ()

@end

@implementation BezierPathController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    CircleScaleView *circleView = [[CircleScaleView alloc] initWithFrame:CGRectZero];
    circleView.backgroundColor = [UIColor redColor];
    circleView.maxValue = 10;
    circleView.currentValue = 5;
    [self.view addSubview:circleView];
    [circleView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.center.equalTo(self.view);
        make.size.mas_equalTo(CGSizeMake(300, 300));
    }];
    [self.view layoutIfNeeded];
    [self addDottedLineBorderWithView:circleView LineWidth:5 lineMargin:10 lineLength:5 lineColor:[UIColor greenColor]];
    
}

/**
*  给视图添加虚线边框
*
*  @param lineWidth  线宽
*  @param lineMargin 每条虚线之间的间距
*  @param lineLength 每条虚线的长度
*  @param lineColor 每条虚线的颜色
*/

- (void)addDottedLineBorderWithView:(UIView *)view LineWidth:(CGFloat)lineWidth lineMargin:(CGFloat)lineMargin lineLength:(CGFloat)lineLength lineColor:(UIColor *)lineColor;
{
    CAShapeLayer *border = [CAShapeLayer layer];
    
    border.strokeColor = lineColor.CGColor;
    
    border.fillColor = nil;
    
    border.path = [UIBezierPath bezierPathWithRect:view.bounds].CGPath;
    
    border.frame = view.bounds;
    
    border.lineWidth = lineWidth;
    
    border.lineCap = @"round";
    
    border.lineDashPattern = @[@(lineLength), @(lineMargin)];
    
    [view.layer addSublayer:border];
}


@end

效果如图 :

截屏2020-01-24下午3.46.04.png

例如按钮单圈的扩散 :

- (void)viewDidLoad {
    
    [super viewDidLoad];
    self.navigationItem.title = @"测试代码控制器";
    
    UIButton *testCodeButton = [UIButton buttonWithType:UIButtonTypeCustom];
    testCodeButton.frame = CGRectMake(CGRectGetMaxX(self.view.frame) / 2 - 70 / 2, CGRectGetMaxY(self.view.frame) / 2 - 30 / 2, 70, 70);
    testCodeButton.backgroundColor = [UIColor blueColor];
    testCodeButton.titleLabel.font = [UIFont systemFontOfSize:15];
    [testCodeButton setTitle:@"测试代码" forState:UIControlStateNormal];
    [testCodeButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
    testCodeButton.layer.cornerRadius = 35;
    [self.view addSubview:testCodeButton];
    [self addAnimation:testCodeButton];

}

///添加动画
- (void)addAnimation:(UIButton *)sender{
    //创建并返回一个新的UIBezierPath对象,该对象初始化时使用指定矩形内接的椭圆路径
    UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(sender.frame.origin.x, sender.frame.origin.y, sender.frame.size.width, sender.frame.size.height)];
    //初始化形状层CAShapeLayer
    CAShapeLayer *senderLayer = [[CAShapeLayer alloc] init];
    //图层中心点在其父图层坐标空间中的位置
    senderLayer.position = CGPointMake(sender.frame.origin.x + sender.frame.size.width / 2, sender.frame.origin.y + sender.frame.size.height / 2);
    //层的边界矩形
    senderLayer.bounds = CGRectMake(sender.frame.origin.x, sender.frame.origin.y, sender.frame.size.width, sender.frame.size.height);
    //层的背景颜色
    senderLayer.backgroundColor = [UIColor clearColor].CGColor;
    //指定形状路径的线宽
    senderLayer.lineWidth = 5;
    //用于绘制形状路径的颜色
    senderLayer.strokeColor = [UIColor blueColor].CGColor;
    //用于填充形状路径的颜色
    senderLayer.fillColor = [UIColor clearColor].CGColor;
    //定义要渲染的形状的路径
    senderLayer.path = path.CGPath;
    //在已属于接收器的其他子层下插入指定的子层
    [self.view.layer insertSublayer:senderLayer below:sender.layer];
    //结束时的矩形
    CGRect endRect = CGRectInset(CGRectMake(sender.frame.origin.x, sender.frame.origin.y, sender.frame.size.height, sender.frame.size.height), -20, -20);
    //根据结束时的矩形创建结束时UIBezierPath对象
    UIBezierPath *endPath = [UIBezierPath bezierPathWithOvalInRect:endRect];
    //要渲染的形状的路径
    senderLayer.path = endPath.CGPath;
    //接收器的不透明度,默认值为1.0
    senderLayer.opacity = 0.0;
    //贝塞尔的开始到结束位置的动画
    CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"path"];
    pathAnimation.fromValue = (__bridge id _Nullable)(path.CGPath);
    pathAnimation.toValue = (__bridge id _Nullable)(endPath.CGPath);
    //动画时长
    pathAnimation.duration = 1.0;
    //动画重复次数
    pathAnimation.repeatCount = HUGE_VALF;
    //贝塞尔的开始到结束透明度的动画
    CABasicAnimation *opacityAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"];
    opacityAnimation.fromValue = [NSNumber numberWithFloat:0.6];
    opacityAnimation.toValue = [NSNumber numberWithFloat:0.0];
    //动画时长
    opacityAnimation.duration = 1.0;
    //动画重复次数
    opacityAnimation.repeatCount = HUGE_VALF;
    
    [senderLayer addAnimation:opacityAnimation forKey:@""];
    [senderLayer addAnimation:pathAnimation forKey:@"path"];
    
}

效果如图 :

Jietu20200310-111004.gif
上一篇下一篇

猜你喜欢

热点阅读