# UIBezierPath_CAShapeLayer **Repository Path**: Lee_Jay/UIBezierPath_CAShapeLayer ## Basic Information - **Project Name**: UIBezierPath_CAShapeLayer - **Description**: UIBezierPath和CAShapeLayer绘图 - **Primary Language**: Objective-C - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 9 - **Forks**: 1 - **Created**: 2016-09-27 - **Last Updated**: 2025-07-28 ## Categories & Tags **Categories**: ios-modules **Tags**: None ## README # 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 ```对象配置完几何路径和绘图属性之后,你就可以使用``` stroke ```和``` fill ```方法在当前的绘图上下文中进行绘制了。``` stroke ```方法将会使用当前的``` strokeColor ```和绘图属性来描绘曲线的轮廓。同样的,``` fill ```方法将会使用``` fillColor ```来填充路径所围成的图形(使用``` UIColor ```类方法来设置``` strokeColor ```和``` fillColor ```)。 ### 画图步骤 使用``` UIBezierPath ```画图步骤: > 1、创建一个``` UIBezierPath ```对象 2、调用``` moveToPoint: ```设置初始线段的起点 3、添加线或者曲线去定义一个或者多个子路径 4、改变``` UIBezierPath ```对象跟绘图相关的属性。如,我们可以设置画笔的属性、填充样式等 ### 创建方法介绍 我们先看看``` UIBezierPath ```类提供了哪些创建方式,这些都是工厂方法,直接使用即可。 创建并且返回一个新的``` UIBezierPath ```对象 ```objc + (instancetype)bezierPath ``` 通过一个矩形, 创建并且返回一个新的``` UIBezierPath ```对象 ```objc /** * 该方法将会创建一个闭合路径, 起始点是 rect 参数的的 origin, 并且按照顺时针针方向添加直线, 最终形成矩形 * @param rect: 矩形路径的 Frame */ + (instancetype)bezierPathWithRect:(CGRect)rect ``` 通过一个指定的矩形中的椭圆形, 创建并且返回一个新的``` UIBezierPath ```对象 ```objc /** * 该方法将会创建一个闭合路径, 该方法会通过顺时针的绘制贝塞尔曲线, 绘制出一个近似椭圆的形状. 如果 rect 参数指定了一个矩形, 那么该 UIBezierPath 对象将会描述一个圆形. * @param rect: 矩形路径的 Frame */ + (instancetype)bezierPathWithOvalInRect:(CGRect)rect ``` 根据一个圆角矩形, 创建并且返回一个新的``` UIBezierPath ```对象 ```objc /** * 该方法将会创建一个闭合路径, 该方法会顺时针方向连续绘制直线和曲线. 当 rect 为正方形时且 cornerRadius 等于边长一半时, 则该方法会描述一个圆形路径. * @param rect:矩形路径的 Frame * @param cornerRadius:矩形的圆角半径 */ + (instancetype)bezierPathWithRoundedRect:(CGRect)rect cornerRadius:(CGFloat)cornerRadius ``` 根据一个圆角矩形, 创建并且返回一个新的``` UIBezierPath ```对象 ```objc /** * 该方法将会创建一个闭合路径, 该方法会顺时针方向连续绘制直线和曲线. * @param rect:矩形路径的 Frame * @param corners:UIRectCorner 枚举类型, 指定矩形的哪个角变为圆角 * @param cornerRadii:矩形的圆角半径 */ + (instancetype)bezierPathWithRoundedRect:(CGRect)rect byRoundingCorners:(UIRectCorner)corners cornerRadii:(CGSize)cornerRadii ``` 通过一个圆弧, 创建并且返回一个新的``` UIBezierPath ```对象 ```objc /** * 该方法会创建出一个开放路径, 创建出来的圆弧是圆的一部分. 在默认的坐标系统中, 开始角度 和 结束角度 都是基于单位圆的(看下面这张图). 调用这个方法之后, 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 ```对象 ```objc + (instancetype)bezierPathWithCGPath:(CGPathRef)CGPath ``` 下面我们一一介绍其用途。 ```objc + (instancetype)bezierPath ``` 这个使用比较多,因为这个工厂方法创建的对象,我们可以根据我们的需要任意定制样式,可以画任何我们想画的图形。 ```objc + (instancetype)bezierPathWithRect:(CGRect)rect ``` 这个工厂方法根据一个矩形画贝塞尔曲线。 ```objc + (instancetype)bezierPathWithOvalInRect:(CGRect)rect ``` 这个工厂方法根据一个矩形画内切曲线。通常用它来画圆或者椭圆。 ```objc + (instancetype)bezierPathWithRoundedRect:(CGRect)rect cornerRadius:(CGFloat)cornerRadius ``` ```objc + (instancetype)bezierPathWithRoundedRect:(CGRect)rect byRoundingCorners:(UIRectCorner)corners cornerRadii:(CGSize)cornerRadii ``` 第一个工厂方法是画矩形,但是这个矩形是可以画圆角的。第一个参数是矩形,第二个参数是圆角大小。 第二个工厂方法功能是一样的,但是可以指定某一个角画成圆角。像这种我们就可以很容易地给``` UIView ```扩展添加圆角的方法了。 ```objc + (instancetype)bezierPathWithArcCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise ``` 这个工厂方法用于画弧,参数说明如下:``` center ```: 弧线中心点的坐标 ``` radius ```: 弧线所在圆的半径``` startAngle ```: 弧线开始的角度值``` endAngle ```: 弧线结束的角度值``` clockwise ```: 是否顺时针画弧线 温馨提示我们下面的代码都是在自定义的``` BezierPathView ```类中下面的方法中调用: ```objc - (void)drawRect:(CGRect)rect ``` 了解坐标 ![](http://upload-images.jianshu.io/upload_images/1321491-71b2f731ec9d22d3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) ### 画三角形 ```objc #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 ```方法: ```objc UIColor *strokeColor = [UIColor greenColor]; [strokeColor set]; ``` 如果我们需要设置填充颜色,比如这里设置为绿色,那么我们需要在设置画笔颜色之前先设置填充颜色,否则画笔颜色就被填充颜色替代了。也就是说,如果要让填充颜色与画笔颜色不一样,那么我们的顺序必须是先设置填充颜色再设置画笔颜色。如下,这两者顺序不能改变。因为我们设置填充颜色也是跟设置画笔颜色一样调用UIColor的-set方法。 ```objc // 设置填充颜色 UIColor *fillColor = [UIColor redColor]; [fillColor set]; [path fill]; // 设置画笔颜色 UIColor *strokeColor = [UIColor greenColor]; [strokeColor set]; ``` ### 画矩形 ```objc #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 ```属性是用来设置线条拐角帽的样式的,其中有三个选择: ```objc /* Line cap styles. */ typedef CF_ENUM(int32_t, CGLineCap) { kCGLineCapButt, kCGLineCapRound, kCGLineCapSquare }; ``` > 其中,第一个是默认的,第二个是轻微圆角,第三个正方形。 ``` lineJoinStyle ```属性是用来设置两条线连结点的样式,其中也有三个选择: ```objc /* Line join styles. */ typedef CF_ENUM(int32_t, CGLineJoin) { kCGLineJoinMiter, kCGLineJoinRound, kCGLineJoinBevel }; ``` > 其中,第一个是默认的表示斜接,第二个是圆滑衔接,第三个是斜角连接。 ### 画圆 我们可以使用``` bezierPathWithOvalInRect: ```方法来画圆,当我们传的``` rect ```参数是一下正方形时,画出来的就是圆。 ```objc #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 ```参数是一下正方形时,画出来的就是圆。那么我们要是不传正方形,那么绘制出来的就是椭圆了。 ```objc #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]; } ``` ### 画圆角矩形 ```objc + (instancetype)bezierPathWithRoundedRect:(CGRect)rect cornerRadius:(CGFloat)cornerRadius + (instancetype)bezierPathWithRoundedRect:(CGRect)rect byRoundingCorners:(UIRectCorner)corners cornerRadii:(CGSize)cornerRadii ``` 第一个工厂方法是画矩形,但是这个矩形是可以画圆角的。第一个参数是矩形,第二个参数是圆角大小。 第二个工厂方法功能是一样的,但是可以指定某一个角画成圆角。像这种我们就可以很容易地给``` UIView ```扩展添加圆角的方法了。 ```objc #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]; } ``` 如果要画只有一个角是圆角,那么我们就修改创建方法: ```objc UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(20, 20, self.frame.size.width - 40, self.frame.size.height - 40) byRoundingCorners:UIRectCornerTopRight cornerRadii:CGSizeMake(20, 20)]; ``` 其中第一个参数一样是传了个矩形,第二个参数是指定在哪个方向画圆角,第三个参数是一个``` CGSize ```类型,用来指定水平和垂直方向的半径的大小。看下效果图: ### 画弧 画弧前,我们需要了解其参考系,如下图: ![](http://upload-images.jianshu.io/upload_images/1321491-d97f0b0ca10bac1a.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) ```objc #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]; } ``` 我们要明确一点,画弧参数``` startAngle ```和``` endAngle ```使用的是弧度,而不是角度,因此我们需要将常用的角度转换成弧度。对于效果图中,我们设置弧的中心为控件的中心,起点弧度为0,也就是正东方向,而终点是135度角的位置。如果设置的``` clockwise:YES ```是逆时针方向绘制,如果设置为``` NO ```,效果如下: ### 画二次贝塞尔曲线 先来学习一下关于控制点,如下图: ![](http://upload-images.jianshu.io/upload_images/1321491-a61946206a461f01.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 画二次贝塞尔曲线,是通过调用此方法来实现的: ```objc - (void)addQuadCurveToPoint:(CGPoint)endPoint controlPoint:(CGPoint)controlPoint ``` >参数说明: ``` endPoint: ```终端点 ``` controlPoint: ```控制点,对于二次贝塞尔曲线,只有一个控制点 ```objc #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 ```函数吧?这两个只是特例而已,其实可以画任意图形,只是想不到,没有做不到的。 ### 画三次贝塞尔曲线 贝塞尔曲线必定通过首尾两个点,称为端点;中间两个点虽然未必要通过,但却起到牵制曲线形状路径的作用,称作控制点。关于三次贝塞尔曲线的控制器,看下图: ![](http://upload-images.jianshu.io/upload_images/1321491-ac191b5d08f570dc.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 提示:其组成是起始端点+控制点1+控制点2+终止端点 如下方法就是画三次贝塞尔曲线的关键方法,以三个点画一段曲线,一般和``` moveToPoint: ```配合使用。 ```objc - (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 ```一起使用。 ### 主要属性 ```objc // 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 ```一样被剪裁掉. 不会出现像素化。当你给``` CAShapeLayer ```做``` 3D ```变换时,它不像一个有寄宿图的普通图层一样变得像素化。 ### 与``` drawRect ```比较 1、 ``` drawRect:```属于``` CoreGraphics ```框架,占用CPU,性能消耗大,不建议重写 2、``` CAShapeLayer: ```属于``` CoreAnimation ```框架,通过GPU来渲染图形,节省性能。动画渲染直接提交给手机GPU,不消耗内存 这两者各有各的用途,而不是说有了``` CAShapeLayer ```就不需要``` drawRect ```。 > 温馨提示:``` drawRect ```只是一个方法而已,是``` UIView ```的方法,重写此方法可以完成我们的绘制图形功能。 ### ``` CAShapeLayer ```与``` UIBezierPath ```的关系 1、``` CAShapeLayer ```中``` shape ```代表形状的意思,所以需要形状才能生效 2、贝塞尔曲线可以创建基于矢量的路径,而``` UIBezierPath ```类是对``` CGPathRef ```的封装 3、贝塞尔曲线给``` CAShapeLayer ```提供路径,``` CAShapeLayer ```在提供的路径中进行渲染。路径会闭环,所以绘制出了``` Shape ``` 4、用于``` CAShapeLayer ```的贝塞尔曲线作为``` path ```,其``` path ```是一个首尾相接的闭环的曲线,即使该贝塞尔曲线不是一个闭环的曲线 ### 画矩形 ```objc #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; } ``` ### 画圆 ```objc #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 ```上呈现出来。 ### 进度条效果 ![这里写图片描述](http://img.blog.csdn.net/20160928145316141) 主要代码如下: ```objc #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效果 ![这里写图片描述](http://img.blog.csdn.net/20160928145532042) 主要代码如下: ```objc #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]; } ``` 我们要实现这个效果,是通过``` strokeStar ```和``` strokeEnd ```这两个属性来完成的,看看官方说明: > 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时,我们看到路径是慢慢消失的。