前言
本篇为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如下:
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如下:
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如下:
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如下:
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如下:
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如下:
- 创建可变路径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、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如下:
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如下:
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如下:
其实,在实际工作中我们用到的更多的是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如下所示:
渐变用来在图形中填充一系列平滑过渡的颜色。可以在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如下:
源码已上传至fenglinyunshi-git,欢迎下载,并提出宝贵意见。
结语
Core Graphics是一个很大的课题,本篇从最基础的知识讲起,后续还会有陆续的篇章推出,敬请期待。
未完待续...
事在人为是一种积极的人生态度,顺其自然是一种达观的生存之道,水到渠成是一种高超的入世智慧,淡泊宁静是一种超脱的生活态度。