UIBezierPath 贝塞尔曲线看这篇就够了

UIBezierPath应用场景

1.绘制图形

2.设置CAKeyFrameAnimation帧动画的动画执行路径

UIBezierPath属性介绍

  • 线宽

/**

  * 线宽属性定义了 `UIBezierPath` 对象中绘制的曲线规格. 默认为: 1.0

  */

@property(nonatomic) CGFloat lineWidth;

  • 曲线终点样式

/**

  * 该属性应用于曲线的终点和起点. 该属性在一个闭合子路经中是无效果的. 默认为: kCGLineCapButt

  */

@property(nonatomic) CGLineCap lineCapStyle;





// CGPath.h

/* Line cap styles. */

typedef CF_ENUM(int32_t, CGLineCap) {

    kCGLineCapButt,

    kCGLineCapRound,

    kCGLineCapSquare

};

image
  • 曲线连接点样式

/**

  * 默认为: kCGLineJoinMiter.

  */

@property(nonatomic) CGLineJoin lineJoinStyle;





// CGPath.h

/* Line join styles. */

typedef CF_ENUM(int32_t, CGLineJoin) {

    kCGLineJoinMiter,

    kCGLineJoinRound,

    kCGLineJoinBevel

};

image
  • 内角和外角距离

/**

  * 两条线交汇处内角和外角之间的最大距离, 只有当连接点样式为 kCGLineJoinMiter

  * 时才会生效,最大限制为10

  * 我们都知道, 两条直线相交时, 夹角越小, 斜接长度就越大.

  * 该属性就是用来控制最大斜接长度的.

  * 当我们设置了该属性, 如果斜接长度超过我们设置的范围, 

  * 则连接处将会以 kCGLineJoinBevel 连接类型进行显示.

  */

@property(nonatomic) CGFloat miterLimit;

image
image
  • UIBezierPath 中的 CGPath 对象

/**

  * 获取这个属性, 你将会获得一个不可变的 CGPathRef 对象,

  * 他可以传入 CoreGraphics 提供的函数中

  * 你可以是用 CoreGraphics 框架提供的方法创建一个路径, 

  * 并给这个属性赋值, 当时设置了一个新的路径后, 

  * 这个将会对你给出的路径对象进行 Copy 操作

  */

@property(nonatomic) CGPathRef CGPath;

  • 绘图路径中的当前点

/**

  * 该属性的值, 将会是下一条绘制的直线或曲线的起始点.

  * 如果当前路径为空, 那么该属性的值将会是 CGPointZero

  */

@property(nonatomic, readonly) CGPoint currentPoint;

  • 渲染精度

/**

  * 该属性用来确定渲染曲线路径的精确度.

  * 该属性的值用来测量真实曲线的点和渲染曲线的点的最大允许距离.

  * 值越小, 渲染精度越高, 会产生相对更平滑的曲线, 但是需要花费更

  * 多的计算时间. 值越大导致则会降低渲染精度, 这会使得渲染的更迅

  * 速. flatness 的默认值为 0.6.

  * Note: 大多数情况下, 我们都不需要修改这个属性的值. 然而当我们

  *       希望以最小的消耗去绘制一个临时的曲线时, 我们也许会临时增

  *       大这个值, 来获得更快的渲染速度.

  */



@property(nonatomic) CGFloat flatness;

  • 是否使用基偶填充规则

/**

  * 设置为 YES, 则路径将会使用 基偶规则 (even-odd) 进行填充.

  * 设置为 NO,  则路径将会使用 非零规则 (non-zero) 规则进行填充.

  */

@property(nonatomic) BOOL usesEvenOddFillRule;

  • 虚线

/**

  * @param pattern: 该属性是一个 C 语言的数组, 其中每一个元素都是 CGFloat

  *                 数组中的元素代表着线段每一部分的长度, 第一个元素代表线段的第一条线,

  *                 第二个元素代表线段中的第一个间隙. 这个数组中的值是轮流的. 来解释一下

  *                 什么叫轮流的. 

  *                 举个例子: 声明一个数组 CGFloat dash[] = @{3.0, 1.0}; 

  *                 这意味着绘制的虚线的第一部分长度为3.0, 第一个间隙长度为1.0, 虚线的

  *                 第二部分长度为3.0, 第二个间隙长度为1.0. 以此类推.



  * @param count: 这个参数是 pattern 数组的个数

  * @param phase: 这个参数代表着, 虚线从哪里开始绘制.

  *                 举个例子: 这是 phase 为 6. pattern[] = @{5, 2, 3, 2}; 那么虚线将会

  *                 第一个间隙的中间部分开始绘制, 如果不是很明白就请继续往下看,

  *                 下文实战部分会对虚线进行讲解.

  */

- (void)setLineDash:(const CGFloat *)pattern

              count:(NSInteger)count

              phase:(CGFloat)phase;

  • 重新获取虚线的模式

/**

  * 该方法可以重新获取之前设置过的虚线样式.

  *  Note:  pattern 这个参数的容量必须大于该方法返回数组的容量.

  *         如果无法确定数组的容量, 那么可以调用两次该方法, 第一次

  *         调用该方法的时候, 传入 count 参数, 然后在用 count 参数

  *         来申请 pattern 数组的内存空间. 然后再第二次正常的调用该方法

  */

- (void)getLineDash:(CGFloat *)pattern 

              count:(NSInteger *)count

              phase:(CGFloat *)phase;

  • 路径是否为空

/**

  * 检测当前路径是否绘制过直线或曲线.

  * Note: 记住, 就算你仅仅调用了 moveToPoint 方法

  *       那么当前路径也被看做不为空.

  */

@property (readonly, getter=isEmpty) BOOL empty;

  • 路径覆盖的矩形区域

/**

  * 该属性描述的是一个能够完全包含路径中所有点

  *  的一个最小的矩形区域. 该区域包含二次贝塞尔

  *  曲线和三次贝塞尔曲线的控制点.

  */

@property (nonatomic, readonly) CGRect bounds;

创建 UIBezierPath的相关方法

1.创建并且返回一个新的 UIBezierPath 对象

+ (instancetype) bezierPath;

2.通过一个矩形, 创建并且返回一个新的 UIBezierPath 对象


/**

  * 该方法将会创建一个闭合路径, 起始点是 rect 参数的的 origin, 并且按照顺时针方向添加直线, 最终形成矩形

  * @param rect:   矩形路径的 Frame

  */

+ (instancetype)bezierPathWithRect:(CGRect)rect;

3.通过一个指定的矩形中的椭圆形, 创建并且返回一个新的 UIBezierPath 对象


/**

  * 该方法将会创建一个闭合路径,  该方法会通过顺时针的绘制贝塞尔曲线, 绘制出一个近似椭圆的形状. 如果 rect 参数指定了一个矩形, 那么该 UIBezierPath 对象将会描述一个圆形.

  * @param rect:   矩形路径的 Frame

  */

+ (instancetype)bezierPathWithOvalInRect:(CGRect)rect;

4.根据一个圆角矩形, 创建并且返回一个新的 UIBezierPath 对象


/**

  * 该方法将会创建一个闭合路径,  该方法会顺时针方向连续绘制直线和曲线.  当 rect 为正方形时且 cornerRadius 等于边长一半时, 则该方法会描述一个圆形路径.

  * @param rect:   矩形路径的 Frame

  * @param cornerRadius:   矩形的圆角半径

  */

+ (instancetype) bezierPathWithRoundedRect:(CGRect)rect 

                              cornerRadius:(CGFloat)cornerRadius;

5.根据一个圆角矩形, 创建并且返回一个新的 UIBezierPath 对象


/**

  * 该方法将会创建一个闭合路径,  该方法会顺时针方向连续绘制直线和曲线.  

  * @param rect:   矩形路径的 Frame

  * @param corners:   UIRectCorner 枚举类型, 指定矩形的哪个角变为圆角

  * @param cornerRadii:   矩形的圆角半径

  */

+ (instancetype) bezierPathWithRoundedRect:(CGRect)rect 

                         byRoundingCorners:(UIRectCorner)corners

                               cornerRadii:(CGSize)cornerRadii;

6.通过一个圆弧, 创建并且返回一个新的 UIBezierPath 对象


/**

  * 该方法会创建出一个开放路径, 创建出来的圆弧是圆的一部分. 在默认的坐标系统中, 开始角度 和 结束角度 都是基于单位圆的(看下面这张图). 调用这个方法之后, currentPoint 将会设置为圆弧的结束点.

  * 举例来说: 指定其实角度为0, 指定结束角度为π, 设置 clockwise 属性为 YES, 将会绘制出圆的下半部分.

  * 然而当我们不修改起始角度 和 结束角度, 我们仅仅将 clockwise 角度设置为 NO, 则会绘制出来一个圆的上半部分.

  * @param center:   圆心

  * @param radius: 半径

  * @param startAngle:   起始角度

  * @param endAngle:   结束角度

  * @param clockwise:   是否顺时针绘制

  */

+ (instancetype) bezierPathWithArcCenter:(CGPoint)center 

                                  radius:(CGFloat)radius 

                              startAngle:(CGFloat)startAngle 

                                endAngle:(CGFloat)endAngle 

                               clockwise:(BOOL)clockwise;

image

7.通过一个 CGPath, 创建并且返回一个新的 UIBezierPath 对象

+ (instancetype) bezierPathWithCGPath:(CGPathRef)CGPath;

8.创建并返回一个新的BezierPath, 这个 BezierPath 的方向是原 BezierPath 的反方向


/**

  * 通过该方法反转一条路径, 并不会修改该路径的样子. 它仅仅是修改了绘制的方向

  * @return: 返回一个新的 UIBezierPath 对象, 形状和原来路径的形状一样,

  *          但是绘制的方向相反.

  */

- (UIBezierPath *) bezierPathByReversingPath;

构造路径的相关方法

1.将 UIBezierPath 对象的 currentPoint 移动到指定的点


/**

  * 如果当前有正在绘制的子路径, 该方法则会隐式的结束当前路径, 

  * 并将 currentPoint 设置为指定点. 当上一条子路径被终止, 该方法

  * 实际上并不会去闭合上一条子路径. 所以上一条自路径的起始点 和

  * 结束点并没有被链接.

  * 对于大多数构造路径相关的方法而言, 在你绘制直线或曲线之前, 需要先调用这个方法.

  * @param point:   当前坐标系统中的某一点

  */

- (void)moveToPoint:(CGPoint)point;

2.在当前子路径中追加一条直线


/**

  * 该方法将会从 currentPoint 到 指定点 链接一条直线. 

  * Note: 在追加完这条直线后, 该方法将会更新 currentPoint 为 指定点

  *       调用该方法之前, 你必须先设置 currentPoint. 如果当前绘制路径

  *       为空, 并且未设置 currentPoint, 那么调用该方法将不会产生任何

  *       效果.

  * @param point:   绘制直线的终点坐标, 当前坐标系统中的某一点

  */

- (void)addLineToPoint:(CGPoint)point;

3.在当前子路径中追加一条圆弧


/**

  * 该方法将会从 currentPoint 添加一条指定的圆弧.

  * 该方法的介绍和构造方法中的一样. 请前往上文查看

  * @param center: 圆心

  * @param radius: 半径

  * @param startAngle: 起始角度

  * @param endAngle: 结束角度

  * @param clockwise: 是否顺时针绘制

  */

- (void)addArcWithCenter:(CGPoint)center 

                  radius:(CGFloat)radius 

              startAngle:(CGFloat)startAngle 

                endAngle:(CGFloat)endAngle 

               clockwise:(BOOL)clockwise NS_AVAILABLE_IOS(4_0);

4.在当前 子路经中追加一条 二次贝塞尔曲线


/**

  * 该方法将会从 currentPoint 到 指定的 endPoint 追加一条二次贝塞尔曲线.

  * currentPoint、endPoint、controlPoint 三者的关系最终定义了二次贝塞尔曲线的形状.

  * 二次贝塞尔曲线的弯曲由一个控制点来控制. 如下图所示

  * Note: 调用该方法前, 你必须先设置 currentPoint, 如果路径为空, 

  *       并且尚未设置 currentPoint, 调用该方法则不会产生任何效果. 

  *       当添加完贝塞尔曲线后, 该方法将会自动更新 currentPoint 为

  *       指定的结束点

  * @param endPoint: 终点

  * @param controlPoint: 控制点

  */

- (void)addQuadCurveToPoint:(CGPoint)endPoint 

               controlPoint:(CGPoint)controlPoint;

image

5.在当前 子路经中追加一条 三次贝塞尔曲线


/**

  * 该方法将会从 currentPoint 到 指定的 endPoint 追加一条三次贝塞尔曲线.

  * 三次贝塞尔曲线的弯曲由两个控制点来控制. 如下图所示

  * Note: 调用该方法前, 你必须先设置 currentPoint, 如果路径为空, 

  *       并且尚未设置 currentPoint, 调用该方法则不会产生任何效果. 

  *       当添加完贝塞尔曲线后, 该方法将会自动更新 currentPoint 为

  *       指定的结束点

  * @param endPoint: 终点

  * @param controlPoint1: 控制点1

  * @param controlPoint2: 控制点2

  */

- (void)addCurveToPoint:(CGPoint)endPoint 

          controlPoint1:(CGPoint)controlPoint1 

          controlPoint2:(CGPoint)controlPoint2;

image

6.关闭当前子路经


/**

  * 该方法将会从 currentPoint 到子路经的起点 绘制一条直线, 

  * 以此来关闭当前的自路径. 紧接着该方法将会更新 currentPoint

  * 为 刚添加的这条直线的终点, 也就是当前子路经的起点.

  */

- (void)closePath;

7.删除 UIBezierPath 对象中的所有点, 效果也就等同于删除了所有子路经

- (void)removeAllPoints;

8.将指定 UIBezierPath 中的内容添加到当前 UIBezierPath 对象中


/**

  * 该方法将会在当前 UIBezierPath 对象的路径中追加

  * 指定的 UIBezierPath 对象中的内容. 

  */

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

9.填充路径


/**

  * 该方法当前的填充颜色 和 绘图属性对路径的封闭区域进行填充.

  * 如果当前路径是一条开放路径, 该方法将会隐式的将路径进行关闭后进行填充

  * 该方法在进行填充操作之前, 会自动保存当前绘图的状态, 所以我们不需要

  * 自己手动的去保存绘图状态了. 

  */

- (void)fill;

10.使用混合模式进行填充


/**

  * 该方法当前的填充颜色 和 绘图属性 (外加指定的混合模式 和 透明度) 

  * 对路径的封闭区域进行填充. 如果当前路径是一条开放路径, 该方法将

  * 会隐式的将路径进行关闭后进行填充

  * 该方法在进行填充操作之前, 会自动保存当前绘图的状态, 所以我们不需要

  * 自己手动的去保存绘图状态了. 

  *

  * @param blendMode: 混合模式决定了如何和已经存在的被渲染过的内容进行合成

  * @param alpha: 填充路径时的透明度

  */

- (void)fillWithBlendMode:(CGBlendMode)blendMode 

                    alpha:(CGFloat)alpha;

11.绘制路径

- (void)stroke;

12.使用混合模式进行填充


/**

  * @param blendMode: 混合模式决定了如何和已经存在的被渲染过的内容进行合成

  * @param alpha: 填充路径时的透明度

  */

- (void)strokeWithBlendMode:(CGBlendMode)blendMode

                      alpha:(CGFloat)alpha;

13.剪切路径


/**

  *  该方法将会修改当前绘图上下文的可视区域.

  *  当调用这个方法之后, 会导致接下来所有的渲染

  *  操作, 只会在剪切下来的区域内进行, 区域外的

  *  内容将不会被渲染.

  *  如果你希望执行接下来的绘图时, 删除剪切区域,

  *  那么你必须在调用该方法前, 先使用 CGContextSaveGState 方法

  *  保存当前的绘图状态, 当你不再需要这个剪切区域

  *  的时候, 你只需要使用 CGContextRestoreGState 方法

  *  来恢复之前保存的绘图状态就可以了.

  * @param blendMode: 混合模式决定了如何和

  *                   已经存在的被渲染过的内容进行合成

  * @param alpha: 填充路径时的透明度

  */

- (void)addClip;

14.是否包含某个点


/**

  *  该方法返回一个布尔值, 当曲线的覆盖区域包含

  * 指定的点(内部点), 则返回 YES, 否则返回 NO. 

  * Note: 如果当前的路径是一个开放的路径, 那么

  *       就算指定点在路径覆盖范围内, 该方法仍然会

  *       返回 NO, 所以如果你想判断一个点是否在一个

  *       开放路径的范围内时, 你需要先Copy一份路径,

  *       并调用 -(void)closePath; 将路径封闭, 然后

  *       再调用此方法来判断指定点是否是内部点.

  * @param point: 指定点.

  */

- (BOOL) containsPoint:(CGPoint)point;

15.Apply Transform


/**

  * 该方法将会直接对路径中的所有点进行指定的放射

  * 变换操作. 

  */

- (void)applyTransform:(CGAffineTransform)transform;

16.设置路径线条颜色


[[UIColor greenColor] set];

[path stroke];

UIBezierPath 应用实践

绘制图形

当需要画图时我们一般创建一个UIView子类, 重写其中的drawRect方法

在drawRect方法中利用UIBezierPath添加画图


#import "BezierView.h"



@implementation BezierView





- (void)drawRect:(CGRect)rect {

    // Drawing code



    //设置线条颜色

    UIColor *color = [UIColor redColor];

    [color set];

    

    //创建UIBezierPath

    UIBezierPath *apath = ({

    

        UIBezierPath *path = [UIBezierPath bezierPath];

        path.lineWidth     = 5.0f;              //设置线条宽度

        path.lineCapStyle  = kCGLineCapRound;   //设置拐角

        path.lineJoinStyle = kCGLineCapRound;  //终点处理

        //设置起始点

        [path moveToPoint:CGPointMake(100, 0)];

        

        //增加线条

        [path addLineToPoint:CGPointMake(200, 40)];

        [path addLineToPoint:CGPointMake(160, 140)];

        [path addLineToPoint:CGPointMake(40, 140)];

        [path addLineToPoint:CGPointMake(0, 40)];

        

        //关闭路径

        [path closePath];

        

        path;

    });

    

    //根据坐标连线

    [apath stroke];

}

然后将自定义的 View添加到 Controller 中


#import "ViewController.h"

#import "BezierView.h"



#define SCREEN_WIDTH [UIScreen mainScreen].bounds.size.width

#define SCREEN_HEIGHT [UIScreen mainScreen].bounds.size.height



@interface ViewController ()



@end



@implementation ViewController



- (void)viewDidLoad {

    

    [super viewDidLoad];

    

    BezierView *beView     = [[BezierView alloc] initWithFrame:\

                                CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)];

    

    beView.backgroundColor = [UIColor whiteColor];

    

    [self.view addSubview:beView];

    

}



@end

image

如果将 drawRect中最后一句话改为[apath fill];运行的结果就是实心图

image

绘制带指定圆角的矩形,如果要设置多个圆角的话就给byRoundingCorners多设置几个角度

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


- (void)drawRect:(CGRect)rect {

    // Drawing code



    //设置线条颜色

    UIColor *color = [UIColor redColor];

    [color set];

    

    //创建UIBezierPath

    UIBezierPath *apath = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(10, 40, 200, 100)

                                                byRoundingCorners:UIRectCornerTopLeft | UIRectCornerTopRight

                                                      cornerRadii:CGSizeMake(100, 100)];

    apath.lineWidth     = 5.0f;

    apath.lineCapStyle  = kCGLineCapRound;

    apath.lineJoinStyle = kCGLineCapRound;

    

    //更具坐标连线

    [apath stroke];

}

image

绘制圆弧


- (void)drawRect:(CGRect)rect {

    // Drawing code



    //设置线条颜色

    UIColor *color = [UIColor redColor];

    [color set];

    

    //创建UIBezierPath

    UIBezierPath *apath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(150, 200)

                                                         radius:100 startAngle:M_PI / 2

                                                       endAngle:M_PI

                                                      clockwise:YES];

    apath.lineWidth     = 5.0f;

    apath.lineCapStyle  = kCGLineCapRound;

    apath.lineJoinStyle = kCGLineCapRound;

    

    //更具坐标连线

    [apath stroke];

}

image

绘制二次贝赛尔曲线


- (void)drawRect:(CGRect)rect {

    // Drawing code



    //设置线条颜色

    UIColor *color = [UIColor redColor];

    [color set];

    

    //创建UIBezierPath

    UIBezierPath *apath = ({

    

        UIBezierPath *path = [UIBezierPath bezierPath];

        path.lineWidth     = 2.0f;              //设置线条宽度

        //path.lineCapStyle = kCGLineCapRound;   //设置拐角

        

        //绘制二次贝赛尔曲线

        

        //设置起始点

        [path moveToPoint:CGPointMake(100, 300)];

        

        //设置EndPoint & Control Point

        [path addQuadCurveToPoint:CGPointMake(300, 300) controlPoint:CGPointMake(100, 10)];

        

        path;

    });



    

    //更具坐标连线

    [apath stroke];

}

image

通过CGPath绘制贝塞尔曲线


- (void)drawRect:(CGRect)rect{    

    //根据CGPathRef绘制贝塞尔曲线

    CGMutablePathRef path = CGPathCreateMutable();

    CGPathMoveToPoint(path, NULL, 10, 440);

    CGPathAddCurveToPoint(path, NULL, 100, 700, 250, 450, 350, 550);

    UIBezierPath *bezierPath_CGPath = [UIBezierPath bezierPathWithCGPath:path];

    bezierPath_CGPath.lineWidth = 2;



   [[UIColor blackColor] set];

    [bezierPath_CGPath stroke];

}

2018-04-0723.15.06

bezierPathByReversingPath方法和applyTransform方法的使用

注意:这个方法并不会改变一条路径的形状, 仅仅是改变了绘制路径的方向


- (void) drawRect:(CGRect)rect {

  

  // 1. 随便画一个路径出来.

  UIBezierPath *path = [UIBezierPath bezierPath];

  [path moveToPoint: CGPointMake(10, 10)];

  [path addLineToPoint: CGPointMake(80, 40)];

  [path addLineToPoint: CGPointMake( 40,  80)];

  [path addLineToPoint: CGPointMake(40, 40)];

  path.lineWidth = 3;



  // 2. 为这条路径制作一个反转路径

  UIBezierPath *reversingPath = [path bezierPathByReversingPath];

  reversingPath.lineWidth = 3;



  // 3. 为了避免两条路径混淆在一起, 我们为第一条路径做一个位移

  CGAffineTransform transform = CGAffineTransformMakeTranslation(200, 0);

  [path applyTransform: transform];



  // 4. 设置颜色, 并绘制路径

  [[UIColor redColor] set];

  [path stroke];

    

  [[UIColor greenColor] set];

  [reversingPath stroke];

}

image

左侧绿色路径就是我们调用 - (UIBezierPath *) bezierPathByReversingPath; 方法反转出来的路径, 右侧则是原路径, 我们可以看到, 两条路径在形状上并没有任何的变化. 那么我们现在来证明一下路径的绘制方向发生了改变. 前文中提到, 当绘制一条直线之后, currentPoint 将会自动更新为这条直线的endPoint. 我们尝试一下分别使用两条路径的currentPoint 与 self.center进行连接, 将刚才的代码修改为下面这个样子:


- (void) drawRect:(CGRect)rect {

  

  // 1. 随便画一个路径出来.

  UIBezierPath *path = [UIBezierPath bezierPath];

  [path moveToPoint: CGPointMake(10, 10)];

  [path addLineToPoint: CGPointMake(80, 40)];

  [path addLineToPoint: CGPointMake( 40,  80)];

  [path addLineToPoint: CGPointMake(40, 40)];

  path.lineWidth = 3;



  // 2. 为这条路径制作一个反转路径

  UIBezierPath *reversingPath = [path bezierPathByReversingPath];

  reversingPath.lineWidth = 3;



  // 3. 为了避免两条路径混淆在一起, 我们为第一条路径做一个位移

  CGAffineTransform transform = CGAffineTransformMakeTranslation(200, 0);

  [path applyTransform: transform];



  // 4. 两条路径分别添加一条直接到 self.center

  [path addLineToPoint: CGPointMake(self.frame.size.width*0.5, self.frame.size.height*0.5)];

  [reversingPath addLineToPoint: CGPointMake(self.frame.size.width*0.5, self.frame.size.height*0.5)];



  // 5. 设置颜色, 并绘制路径

  [[UIColor redColor] set];

  [path stroke];

    

  [[UIColor greenColor] set];

  [reversingPath stroke];

}

image

从图中我们可以清晰的看到, 路径反转以后, 原路径的beginPoint 现在变成了endPoint, 现在大家是否明白了- (UIBezierPath *) bezierPathByReversingPath;方法的作用了呢?

使用setLineDash绘制虚线

@param pattern: 这个参数说白了就是虚线的规格, 你让他多长他就多长, 你让他有多大的间距他就有多大的间距. 是一个 C 语言的数组.

@param count: 官方文档上说就是 pattern 数组的个数. 那你就直接给一个数组的个数就完了, 何必给自己添麻烦呢.

@param phase: 从哪个位置开始绘制虚线, 也就是偏移量, 你给个20, 那他就从20开始绘制虚线.

其实这三个参数上文中已经都介绍过了, 这里在简单的提一嘴, 省的你往上翻了


- (void) typeDashLine {



    // 1. 先创建三条路径, 有对比更有助于理解

    UIBezierPath *path = [UIBezierPath bezierPath];

    [path moveToPoint: CGPointMake(80, 40)];

    [path addLineToPoint: CGPointMake(self.frame.size.width - 40, 40)];

    path.lineWidth = 2;

    

    

    UIBezierPath *path1 = [UIBezierPath bezierPath];

    [path1 moveToPoint: CGPointMake(80, 80)];

    [path1 addLineToPoint: CGPointMake(self.frame.size.width - 40, 80)];

    path1.lineWidth = 2;

    

    

    UIBezierPath *path2 = [UIBezierPath bezierPath];

    [path2 moveToPoint: CGPointMake(80, 120)];

    [path2 addLineToPoint: CGPointMake(self.frame.size.width - 40, 120)];

    path2.lineWidth = 2;

    

    // 2.  这部分是配置三条路径虚线的规格, 重点主要是这部分.

    CGFloat dashLineConfig[] = {8.0, 4.0};

    [path setLineDash: dashLineConfig

                           count: 2

                          phase: 0];

    

    

    CGFloat dashLineConfig1[] = {8.0, 4.0, 16.0, 8.0};

    [path1 setLineDash: dashLineConfig1

                count: 4

                phase: 0];

    

    

    CGFloat dashLineConfig2[] = {8.0, 4.0, 16.0, 8.0};

    [path2 setLineDash: dashLineConfig2

                count: 4

                phase: 12];

    

    // 3. 绘制

    [[UIColor orangeColor] set];

    [path stroke];

    [path1 stroke];

    [path2 stroke];

}

image

设置CAKeyFrameAnimation帧动画的动画执行路径

1.这一步不是必须的,只是为了显示动画执行的路径


#import "testBezierView.h"



@implementation testBezierView



- (void)drawRect:(CGRect)rect{

    UIBezierPath *bezierPath = [UIBezierPath bezierPath];

    bezierPath.lineWidth = 2;

    [bezierPath moveToPoint:CGPointMake(10, 520)];

    //绘制直线

    [bezierPath addLineToPoint:CGPointMake(50, 530)];

    //绘制贝塞尔二次曲线

    [bezierPath addQuadCurveToPoint:CGPointMake(100, 510) controlPoint:CGPointMake(80, 650)];

    //绘制贝塞尔三次曲线

    [bezierPath addCurveToPoint:CGPointMake(200, 530) controlPoint1:CGPointMake(130, 600) controlPoint2:CGPointMake(170, 400)];

    //绘制圆弧

    [bezierPath addArcWithCenter:CGPointMake(300, 400) radius:50 startAngle:0 endAngle:M_PI * 2 clockwise:YES];

    

    //设置路径颜色

    [[UIColor blackColor] set];

    //绘制路径

    [bezierPath stroke];

    

}

@end

2.设置动画执行路径


@interface ViewController ()



@end



@implementation ViewController



- (void)viewDidLoad {

    [super viewDidLoad];

    

    //显示动画执行路径

    testBezierView *testView=[[testBezierView alloc]initWithFrame:CGRectMake(0, 0, [[UIScreen mainScreen]bounds].size.width, [[UIScreen mainScreen]bounds].size.height)];

    testView.backgroundColor=[UIColor whiteColor];

    [self.view addSubview:testView];

    

    //设置动画执行路径

    UIBezierPath *bezierPath = [UIBezierPath bezierPath];

    bezierPath.lineWidth = 2;

    //设置路径起始点

    [bezierPath moveToPoint:CGPointMake(10, 520)];

    //绘制直线

    [bezierPath addLineToPoint:CGPointMake(50, 530)];

    //绘制贝塞尔二次曲线

    [bezierPath addQuadCurveToPoint:CGPointMake(100, 510) controlPoint:CGPointMake(80, 650)];

    //绘制贝塞尔三次曲线

    [bezierPath addCurveToPoint:CGPointMake(200, 530) controlPoint1:CGPointMake(130, 600) controlPoint2:CGPointMake(170, 400)];

    //绘制圆弧

    [bezierPath addArcWithCenter:CGPointMake(300, 400) radius:50 startAngle:0 endAngle:M_PI * 2 clockwise:YES];

    

    //设置小红点

    CALayer* aniLayer = [CALayer layer];

    aniLayer.backgroundColor = [UIColor redColor].CGColor;

    aniLayer.position = CGPointMake(10, 520);

    aniLayer.bounds = CGRectMake(0, 0, 8, 8);

    aniLayer.cornerRadius = 4;

    [self.view.layer addSublayer:aniLayer];

    

    //执行贝塞尔曲线绘制路径的动画

    CAKeyframeAnimation* keyFrameAni = [CAKeyframeAnimation animationWithKeyPath:@"position"];

    keyFrameAni.repeatCount = HUGE_VALF;

    keyFrameAni.path = bezierPath.CGPath;

    keyFrameAni.duration = 15;

    keyFrameAni.beginTime = CACurrentMediaTime() + 1;

    [aniLayer addAnimation:keyFrameAni forKey:@"keyFrameAnimation"];

}

2018-04-07at23.24
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容