4 Star 4 Fork 1

LeeJay / UIBezierPath_CAShapeLayer

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README
MIT

UIBezierPath和CAShapeLayer绘图

UIBezierPath

官方文档

The UIBezierPath class lets you define a path consisting of straight and curved line segments and render that path in your custom views. You use this class initially to specify just the geometry for your path. Paths can define simple shapes such as rectangles, ovals, and arcs or they can define complex polygons that incorporate a mixture of straight and curved line segments. After defining the shape, you can use additional methods of this class to render the path in the current drawing context.

UIBezierPath类允许你在自定义的View中绘制和渲染由直线和曲线组成的路径。你可以在初始化的时候,直接为你的UIBezierPath指定一个几何图形。 路径可以是简单的几何图形例如: 矩形、椭圆、弧线之类的,也可以是相对比较复杂的由直线和曲线组成的多边形。当你定义完图形以后, 你可以使用额外的方法将你的路径直接绘制在当前的绘图上下文中。

UIBezierPath是由几何路径和属性组成的,属性是用来在渲染阶段描绘几何路径的,比如线宽之类的东西。路径和属性是完全独立的,他们并不互相依赖,你可以分开分开去设置他们。一旦你以自己喜欢的方式配置了UIBezierPath对象,你就可以调用方法通知UIBezierPath在当前的绘图上下文中绘制图形了。因为创建、 配置、 渲染路径等操作,都是完全不同的步骤, 所以你可以在你的代码中非常容易的对UIBezierPath对象进行复用。你甚至可以使用同一个UIBezierPath对象去渲染同一个图形很多次,你也可以再多次渲染的间隔中,修改属性来渲染出不同样式的路径。

当你创建了一个空的UIBezierPath对象时,currentPoint这个属性是未定义的,你需要手动的去设置currentPoint。 如果你希望在不绘制任何线条的情况下移动currentPoint,你可以使用moveToPoint:方法。其他的方法都会导致在你的路径中添加额外的直线或曲线。 所有构造路径相关的方法, 都会以当前路径的currentPoint为起点,以你指定的endPoint为终点进行绘制。 当完成绘制之后,会自动将新增的这条线的终点设置为UIBezierPath对象的currentPoint

一个简单的UIBezierPath可以包含许多的开放子路径和封闭子路径。调用closePath方法将会闭合路径,它将会从currentPoint到子路经的firstPoint绘制一条直线。调用moveToPoint:方法将会结束当前的子路径, 但是并不会自动闭合当前的自路径,并且会将currentPoint移动到指定的点,也就是下一条绘制路径的起始点。UIBezierPath中所有的自路径都会共享同样的绘图属性。如果你希望绘制一些子路径,但是不适用相同的绘图属性,那么你就只能创建很多的UIBezierPath对象来承载每一条路径。

当你为UIBezierPath对象配置完几何路径和绘图属性之后,你就可以使用strokefill方法在当前的绘图上下文中进行绘制了。stroke方法将会使用当前的strokeColor和绘图属性来描绘曲线的轮廓。同样的,fill方法将会使用fillColor来填充路径所围成的图形(使用UIColor类方法来设置strokeColorfillColor)。

画图步骤

使用UIBezierPath画图步骤:

1、创建一个UIBezierPath对象 2、调用moveToPoint:设置初始线段的起点 3、添加线或者曲线去定义一个或者多个子路径 4、改变UIBezierPath对象跟绘图相关的属性。如,我们可以设置画笔的属性、填充样式等

创建方法介绍

我们先看看UIBezierPath类提供了哪些创建方式,这些都是工厂方法,直接使用即可。

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

+ (instancetype)bezierPath

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

/**
 * 该方法将会创建一个闭合路径, 起始点是 rect 参数的的 origin, 并且按照顺时针针方向添加直线, 最终形成矩形
 * @param rect:   矩形路径的 Frame
 */
+ (instancetype)bezierPathWithRect:(CGRect)rect

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

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

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

/**
 * 该方法将会创建一个闭合路径,  该方法会顺时针方向连续绘制直线和曲线.  当 rect 为正方形时且 cornerRadius 等于边长一半时, 则该方法会描述一个圆形路径.
 * @param rect:矩形路径的 Frame
 * @param cornerRadius:矩形的圆角半径
 */
+ (instancetype)bezierPathWithRoundedRect:(CGRect)rect
                             cornerRadius:(CGFloat)cornerRadius

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

/**
 * 该方法将会创建一个闭合路径,  该方法会顺时针方向连续绘制直线和曲线.  
 * @param rect:矩形路径的 Frame
 * @param corners:UIRectCorner 枚举类型, 指定矩形的哪个角变为圆角
 * @param cornerRadii:矩形的圆角半径
 */
+  (instancetype)bezierPathWithRoundedRect:(CGRect)rect
                         byRoundingCorners:(UIRectCorner)corners 
                               cornerRadii:(CGSize)cornerRadii

通过一个圆弧, 创建并且返回一个新的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

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

+ (instancetype)bezierPathWithCGPath:(CGPathRef)CGPath

下面我们一一介绍其用途。

+ (instancetype)bezierPath

这个使用比较多,因为这个工厂方法创建的对象,我们可以根据我们的需要任意定制样式,可以画任何我们想画的图形。

+ (instancetype)bezierPathWithRect:(CGRect)rect

这个工厂方法根据一个矩形画贝塞尔曲线。

+ (instancetype)bezierPathWithOvalInRect:(CGRect)rect

这个工厂方法根据一个矩形画内切曲线。通常用它来画圆或者椭圆。

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

第一个工厂方法是画矩形,但是这个矩形是可以画圆角的。第一个参数是矩形,第二个参数是圆角大小。 第二个工厂方法功能是一样的,但是可以指定某一个角画成圆角。像这种我们就可以很容易地给UIView扩展添加圆角的方法了。

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

这个工厂方法用于画弧,参数说明如下:center: 弧线中心点的坐标 radius: 弧线所在圆的半径startAngle: 弧线开始的角度值endAngle: 弧线结束的角度值clockwise: 是否顺时针画弧线

温馨提示我们下面的代码都是在自定义的BezierPathView类中下面的方法中调用:

- (void)drawRect:(CGRect)rect

了解坐标

画三角形

#pragma mark - 画三角形

- (void)drawTrianglePath
{
    UIBezierPath *path = [UIBezierPath bezierPath];
    [path moveToPoint:CGPointMake(20, 20)];
    [path addLineToPoint:CGPointMake(self.frame.size.width - 40, 20)];
    [path addLineToPoint:CGPointMake(self.frame.size.width / 2, self.frame.size.height - 20)];

    // 最后的闭合线是可以通过调用closePath方法来自动生成的,也可以调用-addLineToPoint:方法来添加
    //  [path addLineToPoint:CGPointMake(20, 20)];

    [path closePath];

    // 设置线宽
    path.lineWidth = 1.5;

    // 设置填充颜色
    UIColor *fillColor = [UIColor redColor];
    [fillColor set];
    [path fill];

    // 设置画笔颜色
    UIColor *strokeColor = [UIColor greenColor];
    [strokeColor set];

    // 根据我们设置的各个点连线
    [path stroke];
}

我们设置画笔颜色通过set方法:

UIColor *strokeColor = [UIColor greenColor];
[strokeColor set];

如果我们需要设置填充颜色,比如这里设置为绿色,那么我们需要在设置画笔颜色之前先设置填充颜色,否则画笔颜色就被填充颜色替代了。也就是说,如果要让填充颜色与画笔颜色不一样,那么我们的顺序必须是先设置填充颜色再设置画笔颜色。如下,这两者顺序不能改变。因为我们设置填充颜色也是跟设置画笔颜色一样调用UIColor的-set方法。

// 设置填充颜色
UIColor *fillColor = [UIColor redColor];
[fillColor set];
[path fill];

// 设置画笔颜色
UIColor *strokeColor = [UIColor greenColor];
[strokeColor set];

画矩形

#pragma mark - 画矩形

- (void)drawRectPath
{
    UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(20, 20, self.frame.size.width - 40, self.frame.size.height - 40)];

    path.lineWidth = 1.5;
    path.lineCapStyle = kCGLineCapRound;
    path.lineJoinStyle = kCGLineJoinBevel;

    // 设置填充颜色
    UIColor *fillColor = [UIColor redColor];
    [fillColor set];
    [path fill];

    // 设置画笔颜色
    UIColor *strokeColor = [UIColor greenColor];
    [strokeColor set];

    // 根据我们设置的各个点连线
    [path stroke];
}

lineCapStyle属性是用来设置线条拐角帽的样式的,其中有三个选择:

/* Line cap styles. */
typedef CF_ENUM(int32_t, CGLineCap) {
    kCGLineCapButt,
    kCGLineCapRound,
    kCGLineCapSquare
};

其中,第一个是默认的,第二个是轻微圆角,第三个正方形。

lineJoinStyle属性是用来设置两条线连结点的样式,其中也有三个选择:

/* Line join styles. */

typedef CF_ENUM(int32_t, CGLineJoin) {
    kCGLineJoinMiter,
    kCGLineJoinRound,
    kCGLineJoinBevel
};

其中,第一个是默认的表示斜接,第二个是圆滑衔接,第三个是斜角连接。

画圆

我们可以使用bezierPathWithOvalInRect:方法来画圆,当我们传的rect参数是一下正方形时,画出来的就是圆。

#pragma mark - 画圆

- (void)drawCiclePath
{
    // 传的是正方形,因此就可以绘制出圆了
    UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(20, 20, self.frame.size.width - 40, self.frame.size.width - 40)];

    // 设置填充颜色
    UIColor *fillColor = [UIColor redColor];
    [fillColor set];
    [path fill];

    // 设置画笔颜色
    UIColor *strokeColor = [UIColor greenColor];
    [strokeColor set];

    // 根据我们设置的各个点连线
    [path stroke];
}

注意:要画圆,我们需要传的rect参数必须是正方形哦!

画椭圆

前面我们已经画圆了,我们可以使用bezierPathWithOvalInRect:方法来画圆,当我们传的rect参数是一下正方形时,画出来的就是圆。那么我们要是不传正方形,那么绘制出来的就是椭圆了。

#pragma mark - 画椭圆

- (void)drawOvalPath
{
    // 传的是不是正方形,因此就可以绘制出椭圆圆了
    UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(20, 20, self.frame.size.width - 80, self.frame.size.height - 40)];

    // 设置填充颜色
    UIColor *fillColor = [UIColor redColor];
    [fillColor set];
    [path fill];

    // 设置画笔颜色
    UIColor *strokeColor = [UIColor greenColor];
    [strokeColor set];

    // 根据我们设置的各个点连线
    [path stroke];
}

画圆角矩形

+ (instancetype)bezierPathWithRoundedRect:(CGRect)rect
                             cornerRadius:(CGFloat)cornerRadius

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

第一个工厂方法是画矩形,但是这个矩形是可以画圆角的。第一个参数是矩形,第二个参数是圆角大小。 第二个工厂方法功能是一样的,但是可以指定某一个角画成圆角。像这种我们就可以很容易地给UIView扩展添加圆角的方法了。

#pragma mark - 画带圆角的矩形

- (void)drawRoundedRectPath
{
    UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(20, 20, self.frame.size.width - 40, self.frame.size.height - 40) byRoundingCorners:UIRectCornerTopRight cornerRadii:CGSizeMake(20, 20)];
    // 设置填充颜色
    UIColor *fillColor = [UIColor redColor];
    [fillColor set];
    [path fill];

    // 设置画笔颜色
    UIColor *strokeColor = [UIColor greenColor];
    [strokeColor set];

    // 根据我们设置的各个点连线
    [path stroke];
}

如果要画只有一个角是圆角,那么我们就修改创建方法:

UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(20, 20, self.frame.size.width - 40, self.frame.size.height - 40) byRoundingCorners:UIRectCornerTopRight cornerRadii:CGSizeMake(20, 20)];

其中第一个参数一样是传了个矩形,第二个参数是指定在哪个方向画圆角,第三个参数是一个CGSize类型,用来指定水平和垂直方向的半径的大小。看下效果图:

画弧

画弧前,我们需要了解其参考系,如下图:

#pragma mark - 画弧

- (void)drawARCPath
{
    CGPoint center = CGPointMake(self.frame.size.width / 2, self.frame.size.height / 2);
    UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center
    radius:100
    startAngle:0
    endAngle:((M_PI * 135)/ 180)
    clockwise:YES];

    path.lineCapStyle = kCGLineCapRound;
    path.lineJoinStyle = kCGLineJoinRound;
    path.lineWidth = 5.0;

    UIColor *strokeColor = [UIColor redColor];
    [strokeColor set];

    [path stroke];
}

我们要明确一点,画弧参数startAngleendAngle使用的是弧度,而不是角度,因此我们需要将常用的角度转换成弧度。对于效果图中,我们设置弧的中心为控件的中心,起点弧度为0,也就是正东方向,而终点是135度角的位置。如果设置的clockwise:YES是逆时针方向绘制,如果设置为NO,效果如下:

画二次贝塞尔曲线

先来学习一下关于控制点,如下图:

画二次贝塞尔曲线,是通过调用此方法来实现的:

- (void)addQuadCurveToPoint:(CGPoint)endPoint controlPoint:(CGPoint)controlPoint  

参数说明: endPoint:终端点 controlPoint:控制点,对于二次贝塞尔曲线,只有一个控制点

#pragma mark - 画二次贝塞尔曲线

- (void)drawSecondBezierPath
{
    UIBezierPath *path = [UIBezierPath bezierPath];

    // 首先设置一个起始点
    [path moveToPoint:CGPointMake(20, self.frame.size.height - 100)];

    // 添加二次曲线
    [path addQuadCurveToPoint:CGPointMake(self.frame.size.width - 20, self.frame.size.height - 100)
    controlPoint:CGPointMake(self.frame.size.width / 2, 0)];

    path.lineCapStyle = kCGLineCapRound;
    path.lineJoinStyle = kCGLineJoinRound;
    path.lineWidth = 5.0;

    UIColor *strokeColor = [UIColor redColor];
    [strokeColor set];

    [path stroke];
}

画二次贝塞尔曲线的步骤:

1、先设置一个起始点,也就是通过moveToPoint:设置 2、调用addQuadCurveToPoint:controlPoint:方法设置终端点和控制点,以画二次曲线

在效果图中,拱桥左边的起始点就是我们设置的起始点,最右边的终点,就是我们设置的终端点,而我们设置的控制点为(width / 2, 0)对应于红色矩形中水平方向在正中央,而垂直方向在最顶部。 这个样式看起来很像sin或者cos函数吧?这两个只是特例而已,其实可以画任意图形,只是想不到,没有做不到的。

画三次贝塞尔曲线

贝塞尔曲线必定通过首尾两个点,称为端点;中间两个点虽然未必要通过,但却起到牵制曲线形状路径的作用,称作控制点。关于三次贝塞尔曲线的控制器,看下图:

提示:其组成是起始端点+控制点1+控制点2+终止端点

如下方法就是画三次贝塞尔曲线的关键方法,以三个点画一段曲线,一般和moveToPoint:配合使用。

- (void)addCurveToPoint:(CGPoint)endPoint 
          controlPoint1:(CGPoint)controlPoint1 
          controlPoint2:(CGPoint)controlPoint2

实现代码是这样的:

#pragma mark - 画三次贝塞尔曲线

- (void)drawThirdBezierPath
{
    UIBezierPath *path = [UIBezierPath bezierPath];

    // 设置起始端点
    [path moveToPoint:CGPointMake(20, 150)];

    [path addCurveToPoint:CGPointMake(300, 150)
    controlPoint1:CGPointMake(160, 0)
    controlPoint2:CGPointMake(160, 250)];

    path.lineCapStyle = kCGLineCapRound;
    path.lineJoinStyle = kCGLineJoinRound;
    path.lineWidth = 5.0;

    UIColor *strokeColor = [UIColor redColor];
    [strokeColor set];

    [path stroke];
}

我们需要注意,这里确定的起始端点为(20,150),终止端点为(300, 150),基水平方向是一致的。控制点1的坐标是(160,0),水平方向相当于在中间附近,这个参数可以调整。控制点2的坐标是(160,250),如果以两个端点的连线为水平线,那么就是250-150=100,也就是在水平线下100。这样看起来就像一个sin函数了。

CAShapeLayer

CAShapeLayer继承自CALayer,因此,可使用CALayer的所有属性。但是,CAShapeLayer需要和贝塞尔曲线配合使用才有意义。 官方文档

The shape layer draws a cubic Bezier spline in its coordinate space.The spline is described using a CGPath object and may have both fill and stroke components (in which case the stroke is composited over the fill). The shape as a whole is composited between the layer's contents and its first sublayer.

上面只是部分说明内容,由于较长,只放一部分出来。这里是说CAShapeLayer是在其坐标系统内绘制贝塞尔曲线的。因此,使用CAShapeLayer需要与UIBezierPath一起使用。

主要属性

// CAShapeLayer 绘制的路径
@property(nullable) CGPathRef path;

//路径中的填充颜色
@property(nullable) CGColorRef fillColor;

//填充规则
@property(copy) NSString *fillRule;

//画笔颜色(路径的颜色,边框颜色)
@property(nullable) CGColorRef strokeColor;

//这是一组范围值,路径绘制开始和结束的范围(0 -> 1)
@property CGFloat strokeStart;
@property CGFloat strokeEnd;

//设置虚线显示的起点距离,设置为8,则显示长度8之后的线
@property CGFloat lineDashPhase;

//设置虚线线段的长度和空格的长度,@[@20,@30,@40,@50],画20空30画40空50
@property(nullable, copy) NSArray *lineDashPattern;

//以下属性参见 UIBezierPath 的介绍
@property CGFloat lineWidth;
@property CGFloat miterLimit;
@property(copy) NSString *lineCap;
@property(copy) NSString *lineJoin;

它有一个path属性,而UIBezierPath就是对CGPathRef类型的封装,因此这两者配合起来使用才可以的哦!

CAShapeLayer是一个通过矢量图形而不是bitmap来绘制的图层子类。可以指定颜色、线宽等属性,用CGPath来定义想要绘制的图形,最后CAShapeLayer就会自动渲染出来了。当然,你也可以用Core Graphics直接向原始的CALyer的内容中绘制一个路径drawLayer: inContext: ,相比之下,使用CAShapeLayer有以下一些优点:

渲染快速。CAShapeLayer使用了硬件加速,绘制同一图形会比用Core Graphics快很多。 高效使用内存。一个CAShapeLayer不需要像普通CALayer一样创建一个寄宿图形,所以无论有多大,都不会占用太多的内存。 不会被图层边界剪裁掉。一个CAShapeLayer可以在边界之外绘制。你的图层路径不会像在使用Core Graphics的普通CALayer一样被剪裁掉. 不会出现像素化。当你给CAShapeLayer3D变换时,它不像一个有寄宿图的普通图层一样变得像素化。

drawRect比较

1、 drawRect:属于CoreGraphics框架,占用CPU,性能消耗大,不建议重写

2、CAShapeLayer:属于CoreAnimation框架,通过GPU来渲染图形,节省性能。动画渲染直接提交给手机GPU,不消耗内存

这两者各有各的用途,而不是说有了CAShapeLayer就不需要drawRect

温馨提示:drawRect只是一个方法而已,是UIView的方法,重写此方法可以完成我们的绘制图形功能。

CAShapeLayerUIBezierPath的关系

1、CAShapeLayershape代表形状的意思,所以需要形状才能生效

2、贝塞尔曲线可以创建基于矢量的路径,而UIBezierPath类是对CGPathRef的封装

3、贝塞尔曲线给CAShapeLayer提供路径,CAShapeLayer在提供的路径中进行渲染。路径会闭环,所以绘制出了Shape

4、用于CAShapeLayer的贝塞尔曲线作为path,其path是一个首尾相接的闭环的曲线,即使该贝塞尔曲线不是一个闭环的曲线

画矩形

#pragma mark - 画矩形

- (CAShapeLayer *)drawRect
{
    CAShapeLayer *circleLayer = [CAShapeLayer layer];
    // 指定frame,只是为了设置宽度和高度
    circleLayer.frame = CGRectMake(0, 0, 100, 200);
    // 设置居中显示
    circleLayer.position = self.view.center;
    // 设置填充颜色
    circleLayer.fillColor = [UIColor greenColor].CGColor;
    // 设置线宽
    circleLayer.lineWidth = 2.0;
    // 设置线的颜色
    circleLayer.strokeColor = [UIColor redColor].CGColor;

    // 使用UIBezierPath创建路径
    CGRect frame = CGRectMake(0, 0, 100, 200);
    UIBezierPath *rectPath = [UIBezierPath bezierPathWithRect:frame];

    // 设置CAShapeLayer与UIBezierPath关联
    circleLayer.path = rectPath.CGPath;

    // 将CAShaperLayer放到某个层上显示
    [self.view.layer addSublayer:circleLayer];

    return circleLayer;
}

画圆

#pragma mark - 画矩形

- (CAShapeLayer *)drawRect
{
    CAShapeLayer *circleLayer = [CAShapeLayer layer];
    // 指定frame,只是为了设置宽度和高度
    circleLayer.frame = CGRectMake(0, 0, 100, 200);
    // 设置居中显示
    circleLayer.position = self.view.center;
    // 设置填充颜色
    circleLayer.fillColor = [UIColor greenColor].CGColor;
    // 设置线宽
    circleLayer.lineWidth = 2.0;
    // 设置线的颜色
    circleLayer.strokeColor = [UIColor redColor].CGColor;

    // 使用UIBezierPath创建路径
    CGRect frame = CGRectMake(0, 0, 100, 200);
    UIBezierPath *rectPath = [UIBezierPath bezierPathWithRect:frame];

    // 设置CAShapeLayer与UIBezierPath关联
    circleLayer.path = rectPath.CGPath;

    // 将CAShaperLayer放到某个层上显示
    [self.view.layer addSublayer:circleLayer];

    return circleLayer;
}

注意,我们这里不是放在drawRect:方法中调用的。我们直接将这个CAShaperLayer放到了self.view.layer上,直接呈现出来。

我们创建一个CAShapeLayer,然后配置相关属性,然后再通过UIBezierPath的类方法创建一个内切圆路径,然后将路径指定给CAShapeLayer.path,这就将两者关联起来了。最后,将这个层放到了self.view.layer上呈现出来。

进度条效果

这里写图片描述 主要代码如下:

#pragma mark - 进度条

- (void)drawProgress
{
    CAShapeLayer *grayLayer =[CAShapeLayer layer];
    UIBezierPath *grayPath = [UIBezierPath bezierPathWithArcCenter:self.view.center
    radius:60
    startAngle:M_PI * 3 / 2
    endAngle:M_PI * 7 / 2
    clockwise:YES];
    grayLayer.path = grayPath.CGPath;
    grayLayer.strokeColor = [UIColor grayColor].CGColor;
    grayLayer.fillColor = [UIColor clearColor].CGColor;
    grayLayer.lineWidth = 3;
    [self.view.layer addSublayer:grayLayer];

    grayLayer.strokeStart  = 0;
    grayLayer.strokeEnd = 1;

    CAShapeLayer *greenLayer = [CAShapeLayer layer];
    UIBezierPath *greenPath = [UIBezierPath bezierPathWithArcCenter:self.view.center
    radius:60
    startAngle:M_PI * 3 / 2
    endAngle:M_PI * 7 / 2
    clockwise:YES];
    greenLayer.path = greenPath.CGPath;
    greenLayer.strokeColor = [UIColor greenColor].CGColor;
    greenLayer.fillColor = [UIColor clearColor].CGColor;
    greenLayer.lineWidth = 3;
    [self.view.layer addSublayer:greenLayer];

    greenLayer.strokeStart = 0;
    greenLayer.strokeEnd = 1;

    // 添加动画
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
    animation.duration = 3.0;
    animation.fromValue = @(0);
    animation.toValue = @(1);
    animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    [greenLayer addAnimation:animation forKey:nil];
}

简单Loading效果

这里写图片描述 主要代码如下:

#pragma mark - Loading动画

- (void)drawLoading
{
    UIView *loadingView = [[UIView alloc] initWithFrame:CGRectMake(self.view.frame.size.width/2-50, self.view.frame.size.height/2-50, 100, 100)];
    [self.view addSubview:loadingView];

    // 外层旋转动画
    CABasicAnimation *rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
    rotationAnimation.fromValue = @0.0;
    rotationAnimation.toValue = @(2 * M_PI);
    rotationAnimation.repeatCount = HUGE_VALF;
    rotationAnimation.duration = 3.0;
    rotationAnimation.beginTime = 0.0;

    [loadingView.layer addAnimation:rotationAnimation forKey:@"rotationAnimation"];

    // 内层进度条动画
    CABasicAnimation *strokeAnim1 = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
    strokeAnim1.fromValue = @0.0;
    strokeAnim1.toValue = @1.0;
    strokeAnim1.duration = 1.0;
    strokeAnim1.beginTime = 0.0;
    strokeAnim1.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];

    // 内层进度条动画
    CABasicAnimation *strokeAnim2 = [CABasicAnimation animationWithKeyPath:@"strokeStart"];
    strokeAnim2.fromValue = @0.0;
    strokeAnim2.toValue = @1.0;
    strokeAnim2.duration = 1.0;
    strokeAnim2.beginTime = 1.0;
    strokeAnim2.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];

    CAAnimationGroup *animGroup = [CAAnimationGroup animation];
    animGroup.duration = 2.0;
    animGroup.repeatCount = HUGE_VALF;
    animGroup.fillMode = kCAFillModeForwards;
    animGroup.animations = @[strokeAnim1, strokeAnim2];

    UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(3, 3, CGRectGetWidth(loadingView.frame)-3*2, CGRectGetHeight(loadingView.frame)-3*2)];
    self.loadingLayer = [CAShapeLayer layer];
    self.loadingLayer.lineWidth = 3;
    self.loadingLayer.lineCap = kCALineCapRound;
    self.loadingLayer.strokeColor = [UIColor greenColor].CGColor;
    self.loadingLayer.fillColor = [UIColor clearColor].CGColor;
    self.loadingLayer.strokeStart = 0.0;
    self.loadingLayer.strokeEnd = 1.0;
    self.loadingLayer.path = path.CGPath;
    [self.loadingLayer addAnimation:animGroup forKey:@"strokeAnim"];

    [loadingView.layer addSublayer:self.loadingLayer];
}

我们要实现这个效果,是通过strokeStarstrokeEnd这两个属性来完成的,看看官方说明:

These values define the subregion of the path used to draw the stroked outline. The values must be in the range [0,1] with zero representing the start of the path and one the end. Values in between zero and one are interpolated linearly along the path length. strokeStart defaults to zero and strokeEnd to one. Both are animatable.

这里说明了这两个值的范围是[0,1],当strokeStart的值为0慢慢变成1时,我们看到路径是慢慢消失的。

The MIT License (MIT) Copyright (c) 2016 LeeJay Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

简介

UIBezierPath和CAShapeLayer绘图 展开 收起
Objective-C
MIT
取消

发行版

暂无发行版

贡献者

全部

近期动态

加载更多
不能加载更多了
Objective-C
1
https://gitee.com/Lee_Jay/UIBezierPath_CAShapeLayer.git
git@gitee.com:Lee_Jay/UIBezierPath_CAShapeLayer.git
Lee_Jay
UIBezierPath_CAShapeLayer
UIBezierPath_CAShapeLayer
master

搜索帮助