iOS 开发之Core Graphics(二)详解-基础篇

前言

本篇为Core Graphics系列的第二篇,像第一篇只是在做波纹动画时有用到其相关的知识点,作为本系列的引入。本篇将深入详细的讲解Core Graphics的定义以及相关的API用法,并且形成于代码。

本系列的第一篇初探篇已完结,感兴趣的可以点下面链接:
Core Graphics(一)初探-波纹动画

正文

Core Graphics是一套提供2D绘图功能的C语言的API,使用C结构和C函数模拟了一套面向对象的编程机制,并没有Objective-C对象和方法,而Core Graphics中最重要的“对象”是图形上下文(graphics context),图形上下文是CGContextRef的“对象”,负责存储绘画状态(例如画笔颜色和线条粗细)和绘制内容所处的内存空间。
视图的drawRect:方法在执行之前,系统首先为视图的图层创建一个图形上下文,然后为绘画状态设置一些默认参数。drawRect:方法开始执行时,随着图形上下文不断的执行绘画操作,图层上的内容也会随之改变。drawRect:执行完毕后,系统会将图层与其他图层一起组合成完整的图像并显示在屏幕上。
参与绘图操作的类都定义了改变绘图状态和绘图操作的方法,这些方法其实调用了对应的Core Graphics函数。先发张效果图让大家感受一下:


Core Graphics绘图路径CGPathRef与CGMutablePathRef
  • 创建不可变路径CGPathRef:
    创建不可变路径CGPathRef主要有如下7种方式:
//1、根据已存在的路径绘制路径
CGPathCreateCopy(<#CGPathRef  _Nullable path#>);
//2、根据已存在的路径及transform绘制路径
CGPathCreateCopyByTransformingPath(<#CGPathRef  _Nullable path#>, <#const CGAffineTransform * _Nullable transform#>);
//3、绘制矩形路径
CGPathCreateWithRect(<#CGRect rect#>, <#const CGAffineTransform * _Nullable transform#>);
//4、绘制椭圆(圆是一种特殊的椭圆)
CGPathCreateWithEllipseInRect(<#CGRect rect#>, <#const CGAffineTransform * _Nullable transform#>);
//5、绘制圆角图形
CGPathCreateWithRoundedRect(<#CGRect rect#>, <#CGFloat cornerWidth#>, <#CGFloat cornerHeight#>, <#const CGAffineTransform * _Nullable transform#>);
//6、根据已存在的路径创建一个虚线路径,同时参数`phase', `lengths', and `count'与对应的`CGContextSetLineDash'参数有相同的含义,CGAffineTransform类型,用于在二维空间做旋转,缩放和平移。如transform非空,则在这些参数被添加之前,先进行transform相关的转换。
CGPathCreateCopyByDashingPath(<#CGPathRef  _Nullable path#>, <#const CGAffineTransform * _Nullable transform#>, <#CGFloat phase#>, <#const CGFloat * _Nullable lengths#>, <#size_t count#>);
//7、根据已存在的路径创建一个路径的描边轮廓,同时参数`lineWidth', `lineCap',`lineJoin', and `miterLimit'与对应的cgcontext参数有相同的含义,CGAffineTransform类型,用于在二维空间做旋转,缩放和平移。如transform非空,则在这些参数被添加之前,先进行transform相关的转换。
CGPathCreateCopyByStrokingPath(<#CGPathRef  _Nullable path#>, <#const CGAffineTransform * _Nullable transform#>, <#CGFloat lineWidth#>, <#CGLineCap lineCap#>, <#CGLineJoin lineJoin#>, <#CGFloat miterLimit#>);

下面就按照这七种方式分别详细的讲解一下具体的使用:
1、CGPathCreateCopy(path):

    //创建路径
    CGMutablePathRef path = CGPathCreateMutable();
    CGPathMoveToPoint(path, NULL, 30, 500);
    CGPathAddLineToPoint(path, NULL, 170, 500);
//    CGPathAddCurveToPoint(path, NULL, 220, 400, 270, 600, 320, 500);
    CGPathAddQuadCurveToPoint(path, NULL, 220, 400, 320, 500);

    /*根据已存在的路径绘制路径
     */
    CGPathRef path0 = CGPathCreateCopy(path);
    CGContextSetRGBStrokeColor(self.currentContext, 0.5, 0.1, 0.6, 1);
    CGContextSetLineWidth(self.currentContext, 2);
    CGContextAddPath(self.currentContext, path0);
    CGContextStrokePath(self.currentContext);
    CGPathRelease(path0);

效果图1-1如下:

图1-1.png

2、CGPathCreateCopyByTransformingPath(path, &transform):

    /*根据已存在的路径绘制路径
     *参数1:已存在的路径
     *参数2:CGAffineTransform类型转换
     */
    CGAffineTransform transform = CGAffineTransformMakeTranslation(0,-100);
    CGPathRef path1 = CGPathCreateCopyByTransformingPath(path, &transform);
    CGContextSetRGBStrokeColor(self.currentContext, 0.5, 0.1, 0.6, 1);
    CGContextSetLineWidth(self.currentContext, 2);
    CGContextAddPath(self.currentContext, path1);
    CGContextStrokePath(self.currentContext);
    CGPathRelease(path1);
//注:效果就是在原来的图的基础上向上位移100点

3、CGPathCreateWithRect(CGRectMake(50, 270, 80, 60), NULL):

    /*绘制矩形路径
     *参数1:矩形frame
     *参数2:CGAffineTransform类型转换
     */
    CGPathRef path2 = CGPathCreateWithRect(CGRectMake(50, 270, 80, 60), NULL);
    CGContextSetRGBStrokeColor(self.currentContext, 0.8, 0.6, 0.2, 1);
    CGContextSetLineWidth(self.currentContext, 2);
    CGContextAddPath(self.currentContext, path2);
    CGContextStrokePath(self.currentContext);
    CGPathRelease(path2);

效果图1-2如下:


图1-2.png

4、CGPathCreateWithEllipseInRect(CGRectMake(150, 275, 100, 50), NULL):

    /*绘制椭圆(圆是一种特殊的椭圆)
     *参数1:椭圆的外部frame
     *参数2:CGAffineTransform类型转换
     */
    CGPathRef path3 = CGPathCreateWithEllipseInRect(CGRectMake(150, 275, 100, 50), NULL);
    CGContextSetRGBStrokeColor(self.currentContext, 0.4, 0.9, 0.9, 1);
    CGContextSetLineWidth(self.currentContext, 3);
    CGContextAddPath(self.currentContext, path3);
    CGContextStrokePath(self.currentContext);
    CGPathRelease(path3);

效果图1-3如下:


图1-3.png

5、CGPathCreateWithRoundedRect(CGRectMake(260, 250, 100, 100), 30, 40, NULL):

    /*创建一个圆角图形
     *参数1:路径的frame
     *参数2:圆角的宽
     *参数3:圆角的高
     *参数4:CGAffineTransform类型转换
     */
    CGPathRef path4 = CGPathCreateWithRoundedRect(CGRectMake(260, 250, 100, 100), 30, 40, NULL);
    CGContextSetRGBStrokeColor(self.currentContext, 0.1, 0.6, 0.9, 1);
    CGContextSetLineWidth(self.currentContext, 5);
    CGContextAddPath(self.currentContext, path4);
    CGContextStrokePath(self.currentContext);
    CGPathRelease(path4);

效果图1-4如下:


图1-4.png

6、CGPathCreateCopyByDashingPath(path, NULL, 0, dash, 3):

    /*根据已存在的路径创建一个虚线路径,同时参数`phase', `lengths', and
     `count'与对应的`CGContextSetLineDash'参数有相同的含义,CGAffineTransform类型,用于在二维空间做旋转,缩放和平移。如transform非空,则在这些参数被添加之前,先进行transform相关的转换。
     *参数1:要进行虚线化的不可变路径
     *参数2:CGAffineTransform类型转换
     *参数3:从lengths数组的第几部分开始绘制虚线
     *参数4:C风格的数组 其中为CGFloat值 表示每段虚线的绘制长度 例如传入数组为{10,5},则虚线的先绘制长度为10的实线 在绘制长度为5的空白 在进行循环
     *参数5:count是使用数组的长度,也就是说如果数组长度是4,而count是3,那么只使用数组的前三个
     */
    CGFloat dash[] = {15,20,20,4};
    CGPathRef path5 = CGPathCreateCopyByDashingPath(path, NULL, 0, dash, 3);
    //将路径加在上下文对象上
    CGContextSetRGBStrokeColor(self.currentContext, 0.2, 0.8, 0.5, 1);
    CGContextAddPath(self.currentContext, path5);
    CGContextStrokePath(self.currentContext);
    //释放路径对象
    CGPathRelease(path);
    CGPathRelease(path5);

效果图1-5如下:


图1-5.png

7、CGPathCreateCopyByStrokingPath(path5, NULL, 10, kCGLineCapRound, kCGLineJoinRound, 3):

    /*根据已存在的路径创建一个路径的描边轮廓,同时参数`lineWidth', `lineCap',`lineJoin', and `miterLimit'与对应的cgcontext参数有相同的含义,CGAffineTransform类型,用于在二维空间做旋转,缩放和平移。如transform非空,则在这些参数被添加之前,先进行transform相关的转换。
     *参数1:已存在的不可变路径
     *参数2:CGAffineTransform类型转换
     *参数3:路径宽度
     *参数4:定义线条末端样式
         typedef CF_ENUM(int32_t, CGLineCap) {
             kCGLineCapButt,//指定不绘制端点, 线条结尾处直接结束。(default)
             kCGLineCapRound,//指定绘制圆形端点, 线条结尾处绘制一个直径为线条宽度的半圆
             kCGLineCapSquare//指定绘制方形端点。线条结尾处绘制半个边长为线条宽度的正方形。注意这种形状的端点与“butt”形状的端点十分相似,只是线条比第一种模式长半个线条宽度。
         };
     *参数5:设置线条连接点的风格
         typedef CF_ENUM(int32_t, CGLineJoin) {
             kCGLineJoinMiter,//接合点为尖角(default)
             kCGLineJoinRound,//接合点为圆角
             kCGLineJoinBevel//接合点为斜角
         };
     *参数6:这个值将决定线连接处角的锋利程度当参数5为kCGLineJoinMiter时才起作用
     */
    CGPathRef path6 = CGPathCreateCopyByStrokingPath(path5, NULL, 10, kCGLineCapRound, kCGLineJoinRound, 3);
    //将路径加在上下文对象上
    CGContextSetRGBStrokeColor(self.currentContext, 0.6, 0.4, 0.5, 1);
    CGContextSetLineWidth(self.currentContext, 2);
    CGContextAddPath(self.currentContext, path6);
    CGContextStrokePath(self.currentContext);
    //释放路径对象
    CGPathRelease(path);
    CGPathRelease(path5);
    CGPathRelease(path6);

效果图1-6如下:

图1-6.png
  • 创建可变路径CGMutablePathRef
    创建可变路径CGMutablePathRef主要有如下3种方式:
//1、直接创建
CGPathCreateMutable();
//2、创建已存在路径的一个副本
CGPathCreateMutableCopy(<#CGPathRef  _Nullable path#>);
//3、创建已存在路径的一个副本,并传入相对已存在的路径CGAffineTransform对象的位移,缩放或者旋转变换
CGPathCreateMutableCopyByTransformingPath(<#CGPathRef  _Nullable path#>, <#const CGAffineTransform * _Nullable transform#>);

下面就按照这三种方式分别详细的讲解一下具体的使用:
1、CGPathCreateMutable():

    //设置填充颜色
    CGContextSetRGBFillColor(self.currentContext, 0.6,0.5,0.3,1);//或
    //CGContextSetFillColor(currentContext, CGColorGetComponents([UIColor greenColor].CGColor));
    //设置路径颜色
    CGContextSetRGBStrokeColor(self.currentContext, 0.2, 0.8, 0.5, 1);//或
    //CGContextSetStrokeColor(currentContext, CGColorGetComponents([UIColor redColor].CGColor));
    
    /*
     *创建一个可变路径
     */
    CGMutablePathRef path = CGPathCreateMutable();
    CGPathMoveToPoint(path, NULL, 50, 100);
    CGPathAddLineToPoint(path, NULL, 150, 200);
    CGPathAddLineToPoint(path, NULL, 50, 200);
    //将路径加到当前的上下文
    CGContextAddPath(self.currentContext, path);
    //关闭路径
    CGContextClosePath(self.currentContext);
    //如果单独的设置只会应用前者填充颜色或者路径颜色之一
    //CGContextStrokePath(self.currentContext);
    //CGContextFillPath(self.currentContext);
    CGContextDrawPath(self.currentContext, kCGPathFillStroke);
    CGPathRelease(path);

效果图2-1如下:


图2-1.png

2、CGPathCreateMutableCopy(path):

    //设置填充颜色
    CGContextSetRGBFillColor(self.currentContext, 0.6,0.5,0.3,1);//或
    //CGContextSetFillColor(currentContext, CGColorGetComponents([UIColor greenColor].CGColor));
    //设置路径颜色
    CGContextSetRGBStrokeColor(self.currentContext, 0.2, 0.8, 0.5, 1);//或
    //CGContextSetStrokeColor(currentContext, CGColorGetComponents([UIColor redColor].CGColor));
    
    /*
     *创建一个可变路径
     */
    CGMutablePathRef path = CGPathCreateMutable();
    CGPathMoveToPoint(path, NULL, 50, 100);
    CGPathAddLineToPoint(path, NULL, 150, 200);
    CGPathAddLineToPoint(path, NULL, 50, 200);
/*
     *创建已存在路径的一个副本
     */
    CGMutablePathRef path0 = CGPathCreateMutableCopy(path);
    CGPathAddQuadCurveToPoint(path0, NULL, 200, 50, 200, 100);
    //将路径加到当前的上下文
    CGContextAddPath(self.currentContext, path0);
    CGContextClosePath(self.currentContext);
    /*CGPathDrawingMode枚举
     *typedef CF_ENUM (int32_t, CGPathDrawingMode) {
         kCGPathFill,//只填充而路径没有颜色(表示用非零绕数规则)
         kCGPathEOFill,//使用奇偶规则填充当前路径
         kCGPathStroke,//只有边框
         kCGPathFillStroke,//表示既描线又填充
         kCGPathEOFillStroke//奇偶填充并绘制边框
      };
     *非零绕数规则,假如一个点被从左到右跨过,计数器+1,从右到左跨过,计数器-1,最后,如果结果是0,那么不填充,如果是非零,那么填充。
     *奇偶规则,假如一个点被跨过,那么+1,最后是奇数,那么要被填充,偶数则不填充,和方向没有关系。
     */

    CGContextDrawPath(self.currentContext, kCGPathFillStroke);
    CGPathRelease(path);
    CGPathRelease(path0);

效果图2-2如下:


图2-2.png

3、CGPathCreateMutableCopyByTransformingPath(path0, &transform):

    /*创建已存在路径的一个副本,并传入相对已存在的路径CGAffineTransform对象的位移,缩放或者旋转变换
     *参数1:已存在路径
     *参数2:CGAffineTransform对象的位移,缩放或者旋转变换
     */
    CGAffineTransform transform = CGAffineTransformMakeScale(1.5, 1.5);
    CGMutablePathRef path1 = CGPathCreateMutableCopyByTransformingPath(path0, &transform);
    CGContextAddPath(self.currentContext, path1);
    //关闭路径
    CGContextClosePath(self.currentContext);
    CGContextDrawPath(self.currentContext, kCGPathFillStroke);
    //释放路径对象
    CGPathRelease(path);
    CGPathRelease(path0);
    CGPathRelease(path1);

效果图2-3如下:


图2-3.png
Core Graphics直接用CGContextRef绘制路径和图案

除了通过CGPathRef与CGMutablePathRef绘制路径和图案外,我们还可以直接用CGContextRef直接来绘制点路径,椭圆,扇形,折现等等。同时我们可以设置路径的边框颜色,填充颜色,是否是虚线,交点的样式等等属性。
废话少说直接上代码:

    //绘制三角形(同下面注释掉的三行)
    CGPoint points[] = {
        CGPointMake(100,100),
        CGPointMake(150,200),
        CGPointMake(50,200)
    };
    CGContextAddLines(self.currentContext, points, 3);
//    CGContextMoveToPoint(self.currentContext, 100, 100);
//    CGContextAddLineToPoint(self.currentContext, 150, 200);
//    CGContextAddLineToPoint(self.currentContext, 50, 200);
    //设置填充颜色
    [[UIColor purpleColor] setFill];
    //设置路径颜色
    [[UIColor blackColor] setStroke];
    CGContextClosePath(self.currentContext);
    CGContextDrawPath(self.currentContext, kCGPathFillStroke);
    
    //绘制椭圆(圆形是特殊的椭圆)
    CGContextAddEllipseInRect(self.currentContext, CGRectMake(180, 100, 150, 100));
    //设置填充颜色
    CGContextSetRGBFillColor(self.currentContext, 0.6,0.5,0.3,1);//或
    //CGContextSetFillColor(currentContext, CGColorGetComponents([UIColor greenColor].CGColor));
    //设置路径颜色
    CGContextSetRGBStrokeColor(self.currentContext, 0.2, 0.8, 0.5, 1);//或
    //CGContextSetStrokeColor(currentContext, CGColorGetComponents([UIColor redColor].CGColor));
    CGContextDrawPath(self.currentContext,kCGPathFillStroke);
    
    //绘制扇形
    float radius = 50;
    CGPoint center = CGPointMake(100, 300);
    CGContextMoveToPoint(self.currentContext, center.x, center.y);
    /*参数1:上下文
     *参数2,3:扇形的圆心
     *参数4:扇形的半径
     *参数5:顺时针还是逆时针,1表示顺时针,0表示逆时针(由于Core Graphics的坐标系和UIKit坐标系不同,我们实际看到的视图1表示逆时针,0表示顺时针)
     */
    CGContextAddArc(self.currentContext, center.x, center.y, radius, M_PI_2, M_PI, 1);
    [[UIColor blueColor] setFill];
    [[UIColor cyanColor] setStroke];
    CGContextClosePath(self.currentContext);
    CGContextDrawPath(self.currentContext,kCGPathFillStroke);
    
    //绘制三次贝塞尔曲线
    CGContextMoveToPoint(self.currentContext, 200, 300);
    /*参数1:上下文
     *参数2,3:曲线第一个控制点的x和y坐标
     *参数4,5:曲线第二个控制点的x和y坐标
     *参数6:路径的终点
     */
    CGContextAddCurveToPoint(self.currentContext, 250, 250, 300, 350, 350, 300);
    [[UIColor whiteColor] setFill];
    [[UIColor brownColor] setStroke];
    CGContextClosePath(self.currentContext);
    CGContextDrawPath(self.currentContext,kCGPathFillStroke);
    
    //绘制二次贝塞尔曲线
    CGContextMoveToPoint(self.currentContext, 50, 400);
    /*参数1:上下文
     *参数2,3:曲线控制点的x和y坐标
     *参数4:路径的终点
     */
    CGContextAddQuadCurveToPoint(self.currentContext, 100, 450, 150, 400);
    CGFloat len[] = {5,4,3,6};
    //设置路径宽度
    CGContextSetLineWidth(self.currentContext, 4);
    //设置路径虚线
    CGContextSetLineDash(self.currentContext, 0, len, 4);
    //设置路径颜色
    [[UIColor redColor] setStroke];
    //    CGContextClosePath(self.currentContext);
    CGContextDrawPath(self.currentContext,kCGPathStroke);

效果图3-1如下:


图3-1.png

其实,在实际工作中我们用到的更多的是UIBezierPath和UIcolor,UIBezierPath和UIColor在Core Graphics中有对应的C结构:CGMutablePathRef和CGColorRef。通常情况下,使用Objective-C类更加方便,后续笔者将用专门的篇幅来介绍UIBezierPath的具体用法。
但是,有些功能只能使用Core Graphics完成,例如绘制渐变。Core Graphics中的结构和函数都具有CG前缀,如果遇到无法使用Objective-C类完成的绘图功能,可以在文档中查阅带有CG前缀的结构和函数,直接使用Core Graphics。
我们会看到很多Core Graphics类型都带有Ref后缀。带有Ref后缀的类型是Core Graphics中用来模拟面向对象机制的C结构。Core Graphics“对象”与Objective-C对象都是在堆上分配内存,因此创建一个Core Graphics"对象"时,同样会返回一个指向对象内存地址的指针。
使用这种分配方式的C结构都有一个用来表示结构指针(结构名后加一个“*”)的类型定义。例如,CGColor结构(不会直接使用的类型)有一个表示CGColor *的类型定义—CGColorRef(应该使用的类型)。使用这种类型定义是为了区分指针变量,方便开发者判断指针变量是指向C结构还是可以接收消息的Objective-C对象。
相反,部分类型没有结构指针,因此类型名称不带Ref后缀,例如CGRect和CGPoint。这些类型的数据结构简单,可以直接在栈上分配,因此不需要使用结构指针。
带有Ref后缀类型的对象可能具有指向其他Core Graphics“对象”的强引用指针,并成为这些“对象”的拥有者。但是ARC无法识别这类强引用和“对象”所有权,必须在使用完之后手动释放。规则是,如果使用名称中带有create或者copy的函数创建了一个Core Graphics“对象”,就必须调用对应的Release函数并传入该对象指针。

阴影和渐变

到目前为止,我们还没有办法绘制阴影和渐变,只能使用Core Graphics。绘制阴影之前,需要将阴影效果添加到一个图形上下文中,之后在该图形上下文中绘制的所有不透明图象都会带有阴影效果。可以为阴影设置偏移量(使用CGSize表示)和模糊指数(使用CGFloat表示)。
请注意,没有删除阴影效果的函数。因此需要在添加阴影效果之前保存绘画状态,之后再恢复没有阴影效果的状态。
废话少说直接上代码:

    //保存添加阴影效果之前的绘画状态
    CGContextSaveGState(self.currentContext);
    //设置阴影的偏移量和模糊指数以及颜色(可以使用默认颜色,也可以使用自己定义的颜色)
    CGContextSetShadow(self.currentContext, CGSizeMake(5, 10), 3);
    //CGContextSetShadowWithColor(self.currentContext, CGSizeMake(5, 10), 5, [UIColor cyanColor].CGColor);
    //在这里绘制的图像会带有阴影效果
    ......
    //恢复没有阴影效果的状态
    CGContextRestoreGState(self.currentContext);
    //在这里绘制的图像没有带有阴影效果
    ......

效果图4-1如下所示:


图4-1.png

渐变用来在图形中填充一系列平滑过渡的颜色。可以在CGGradientRef中设置需要的颜色和渐变的方式(线性渐变和径向渐变)。与填充颜色不同,无法用渐变填充路径,渐变会直接填满整个图形上下文,因此,如果需要将渐变应用在制定的范围内,必须使用剪切路径裁剪图形上下文。同时,与绘制阴影时的情况类似,没有函数可以删除剪切路径,同样需要在使用剪切路径前保存绘画状态,填充渐变之后再恢复状态。
废话少说直接上代码:

    CGContextRef currentContext = UIGraphicsGetCurrentContext();
    /*
     *渐变
     */
    //保存渐变之前的绘画状态
    CGContextSaveGState(self.currentContext);
    
    //绘制渐变剪切路径
    UIBezierPath *path1 = [[UIBezierPath alloc] init];
    [path1 moveToPoint:CGPointMake(100, 450)];
    [path1 addLineToPoint:CGPointMake(250, 450)];
    [path1 addLineToPoint:CGPointMake(250, 650)];
    [path1 addLineToPoint:CGPointMake(100, 650)];
    [path1 closePath];
    //是用剪切路径剪裁图形上下文
    [path1 addClip];
    
    //绘制渐变
    CGFloat locations[4] = {0.0,0.4,0.7,1.0};//三个颜色节点
    CGFloat components[16] = {1.0,0.3,0.0,1.0,//起始颜色
                              0.2,0.8,0.2,1.0,//中间颜色
                              1.0,1.0,0.5,1.0,//中间颜色
                              0.8,0.3,0.4,1.0};//终止颜色
    //创建RGB色彩空间对象
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, components, locations, 4);
    
    //渐变的起点(渐变效果在以起点和终点为轴的直线周边)
//    CGPoint startPoint = CGPointMake(100, 450);
    //渐变的终点
//    CGPoint endPoint =    CGPointMake(250, 650);
    /*线性渐变
     *参数1:当前上下文
     *参数2:渐变指针
     *参数3,4:渐变的起始和终止位置
     *参数5:CGGradientDrawingOptions枚举
     typedef CF_OPTIONS (uint32_t, CGGradientDrawingOptions) {
     kCGGradientDrawsBeforeStartLocation = (1 << 0),//扩展整个渐变到渐变的起点之前的所有点
     kCGGradientDrawsAfterEndLocation = (1 << 1)//扩展整个渐变到渐变的终点之后的所有点
     };
     0表示既不往前扩展也不往后扩展
     */
//    CGContextDrawLinearGradient(self.currentContext, gradient, startPoint, endPoint, kCGGradientDrawsBeforeStartLocation|kCGGradientDrawsAfterEndLocation);
    
    /*径向渐变
     *参数1:当前上下文
     *参数2:渐变指针
     *参数3:渐变的起始圆心
     *参数4:渐变的起始半径
     *参数5:渐变的终止圆心
     *参数6:渐变的终止半径
     *参数5:CGGradientDrawingOptions枚举
     typedef CF_OPTIONS (uint32_t, CGGradientDrawingOptions) {
     kCGGradientDrawsBeforeStartLocation = (1 << 0),//扩展整个渐变到渐变的起点之前的所有点
     kCGGradientDrawsAfterEndLocation = (1 << 1)//扩展整个渐变到渐变的终点之后的所有点
     };
     0表示既不往前扩展也不往后扩展
     */
    CGContextDrawRadialGradient(self.currentContext, gradient, CGPointMake(175, 550), 20, CGPointMake(175, 580), 60, 0);
    
    //释放创建的C结构对象
    CGGradientRelease(gradient);
    CGColorSpaceRelease(colorSpace);
    //恢复绘画状态
    CGContextRestoreGState(self.currentContext);

效果图4-2如下:

图4-2.png

源码已上传至fenglinyunshi-git,欢迎下载,并提出宝贵意见。

结语

Core Graphics是一个很大的课题,本篇从最基础的知识讲起,后续还会有陆续的篇章推出,敬请期待。

未完待续...

事在人为是一种积极的人生态度,顺其自然是一种达观的生存之道,水到渠成是一种高超的入世智慧,淡泊宁静是一种超脱的生活态度。

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

推荐阅读更多精彩内容