2023年7月13日发(作者:)
iOS开发之CoreGraphics(⼆)详解-基础篇前⾔本篇为Core Graphics系列的第⼆篇,像第⼀篇只是在做波纹动画时有⽤到其相关的知识点,作为本系列的引⼊。本篇将深⼊详细的讲解CoreGraphics的定义以及相关的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类型,⽤于在⼆维空间CGPathCreateCopyByDashingPath(<#CGPathRef _Nullable path#>, <#const CGAffineTransform * _Nullable transform#>, <#CGFloat phase#>, <#const CGFloat * _Nullabl//7、根据已存在的路径创建⼀个路径的描边轮廓,同时参数`lineWidth', `lineCap',`lineJoin', and `miterLimit'与对应的cgcontext参数有相同的含义,CGAffineTransform类型,⽤于在CGPathCreateCopyByStrokingPath(<#CGPathRef _Nullable path#>, <#const CGAffineTransform * _Nullable transform#>, <#CGFloat lineWidth#>, <#CGLineCap lineCap#下⾯就按照这七种⽅式分别详细的讲解⼀下具体的使⽤: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(tContext, 0.5, 0.1, 0.6, 1); CGContextSetLineWidth(tContext, 2); CGContextAddPath(tContext, path0); CGContextStrokePath(tContext); CGPathRelease(path0);效果图1-1如下:图2、CGPathCreateCopyByTransformingPath(path, &transform): /*根据已存在的路径绘制路径 *参数1:已存在的路径 *参数2:CGAffineTransform类型转换 */ CGAffineTransform transform = CGAffineTransformMakeTranslation(0,-100); CGPathRef path1 = CGPathCreateCopyByTransformingPath(path, &transform); CGContextSetRGBStrokeColor(tContext, 0.5, 0.1, 0.6, 1); CGContextSetLineWidth(tContext, 2); CGContextAddPath(tContext, path1); CGContextStrokePath(tContext); 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(tContext, 0.8, 0.6, 0.2, 1); CGContextSetLineWidth(tContext, 2); CGContextAddPath(tContext, path2); CGContextStrokePath(tContext); 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(tContext, 0.4, 0.9, 0.9, 1); CGContextSetLineWidth(tContext, 3); CGContextAddPath(tContext, path3); CGContextStrokePath(tContext); 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(tContext, 0.1, 0.6, 0.9, 1); CGContextSetLineWidth(tContext, 5); CGContextAddPath(tContext, path4); CGContextStrokePath(tContext); CGPathRelease(path4);效果图1-4如下:图6、CGPathCreateCopyByDashingPath(path, NULL, 0, dash, 3): /*根据已存在的路径创建⼀个虚线路径,同时参数`phase', `lengths', and *参数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(tContext, 0.2, 0.8, 0.5, 1); CGContextAddPath(tContext, path5); CGContextStrokePath(tContext); //释放路径对象 CGPathRelease(path); CGPathRelease(path5); `count'与对应的`CGContextSetLineDash'参数有相同的含义,CGAffineTransform类型,⽤于在⼆维空间做旋转,缩放和平移。如transform⾮空,则在这些参数被添加之前,先进效果图1-5如下:图7、CGPathCreateCopyByStrokingPath(path5, NULL, 10, kCGLineCapRound, kCGLineJoinRound, 3): /*根据已存在的路径创建⼀个路径的描边轮廓,同时参数`lineWidth', `lineCap',`lineJoin', and `miterLimit'与对应的cgcontext参数有相同的含义,CGAffineTransform类型,⽤于在 *参数1:已存在的不可变路径 *参数2:CGAffineTransform类型转换 *参数3:路径宽度 *参数4:定义线条末端样式 typedef CF_ENUM(int32_t, CGLineCap) { kCGLineCapButt,//指定不绘制端点, 线条结尾处直接结束。(default) kCGLineCapRound,//指定绘制圆形端点, 线条结尾处绘制⼀个直径为线条宽度的半圆 }; *参数5:设置线条连接点的风格 typedef CF_ENUM(int32_t, CGLineJoin) { kCGLineJoinMiter,//接合点为尖⾓(default) kCGLineJoinRound,//接合点为圆⾓ kCGLineJoinBevel//接合点为斜⾓ }; *参数6:这个值将决定线连接处⾓的锋利程度当参数5为kCGLineJoinMiter时才起作⽤ */ CGPathRef path6 = CGPathCreateCopyByStrokingPath(path5, NULL, 10, kCGLineCapRound, kCGLineJoinRound, 3); //将路径加在上下⽂对象上 CGContextSetRGBStrokeColor(tContext, 0.6, 0.4, 0.5, 1); CGContextSetLineWidth(tContext, 2); CGContextAddPath(tContext, path6); CGContextStrokePath(tContext); //释放路径对象 CGPathRelease(path); CGPathRelease(path5); CGPathRelease(path6); kCGLineCapSquare//指定绘制⽅形端点。线条结尾处绘制半个边长为线条宽度的正⽅形。注意这种形状的端点与“butt”形状的端点⼗分相似,只是线条⽐第⼀种模式长半个效果图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(tContext, 0.6,0.5,0.3,1);//或 //CGContextSetFillColor(currentContext, CGColorGetComponents([UIColor greenColor].CGColor)); //设置路径颜⾊ CGContextSetRGBStrokeColor(tContext, 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(tContext, path); //关闭路径 CGContextClosePath(tContext); //如果单独的设置只会应⽤前者填充颜⾊或者路径颜⾊之⼀ //CGContextStrokePath(tContext); //CGContextFillPath(tContext); CGContextDrawPath(tContext, kCGPathFillStroke); CGPathRelease(path);效果图2-1如下:图2、CGPathCreateMutableCopy(path): //设置填充颜⾊ CGContextSetRGBFillColor(tContext, 0.6,0.5,0.3,1);//或 //CGContextSetFillColor(currentContext, CGColorGetComponents([UIColor greenColor].CGColor)); //设置路径颜⾊ CGContextSetRGBStrokeColor(tContext, 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(tContext, path0); CGContextClosePath(tContext); /*CGPathDrawingMode枚举 *typedef CF_ENUM (int32_t, CGPathDrawingMode) { kCGPathFill,//只填充⽽路径没有颜⾊(表⽰⽤⾮零绕数规则) kCGPathEOFill,//使⽤奇偶规则填充当前路径 kCGPathStroke,//只有边框 kCGPathFillStroke,//表⽰既描线⼜填充 kCGPathEOFillStroke//奇偶填充并绘制边框 }; *⾮零绕数规则,假如⼀个点被从左到右跨过,计数器+1,从右到左跨过,计数器-1,最后,如果结果是0,那么不填充,如果是⾮零,那么填充。 *奇偶规则,假如⼀个点被跨过,那么+1,最后是奇数,那么要被填充,偶数则不填充,和⽅向没有关系。 */ CGContextDrawPath(tContext, 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(tContext, path1); //关闭路径 CGContextClosePath(tContext); CGContextDrawPath(tContext, kCGPathFillStroke); //释放路径对象 CGPathRelease(path); CGPathRelease(path0); CGPathRelease(path1);效果图2-3如下:图e Graphics直接⽤CGContextRef绘制路径和图案除了通过CGPathRef与CGMutablePathRef绘制路径和图案外,我们还可以直接⽤CGContextRef直接来绘制点路径,椭圆,扇形,折现等等。同时我们可以设置路径的边框颜⾊,填充颜⾊,是否是虚线,交点的样式等等属性。废话少说直接上代码: //绘制三⾓形(同下⾯注释掉的三⾏) CGPoint points[] = { CGPointMake(100,100), CGPointMake(150,200), CGPointMake(50,200) }; CGContextAddLines(tContext, points, 3);// CGContextMoveToPoint(tContext, 100, 100);// CGContextAddLineToPoint(tContext, 150, 200);// CGContextAddLineToPoint(tContext, 50, 200); //设置填充颜⾊ [[UIColor purpleColor] setFill]; //设置路径颜⾊ [[UIColor blackColor] setStroke]; CGContextClosePath(tContext); CGContextDrawPath(tContext, kCGPathFillStroke);
//绘制椭圆(圆形是特殊的椭圆) CGContextAddEllipseInRect(tContext, CGRectMake(180, 100, 150, 100)); //设置填充颜⾊ CGContextSetRGBFillColor(tContext, 0.6,0.5,0.3,1);//或 //CGContextSetFillColor(currentContext, CGColorGetComponents([UIColor greenColor].CGColor)); //CGContextSetFillColor(currentContext, CGColorGetComponents([UIColor greenColor].CGColor)); //设置路径颜⾊ CGContextSetRGBStrokeColor(tContext, 0.2, 0.8, 0.5, 1);//或 //CGContextSetStrokeColor(currentContext, CGColorGetComponents([UIColor redColor].CGColor)); CGContextDrawPath(tContext,kCGPathFillStroke);
//绘制扇形 float radius = 50; CGPoint center = CGPointMake(100, 300); CGContextMoveToPoint(tContext, center.x, center.y); /*参数1:上下⽂ *参数2,3:扇形的圆⼼ *参数4:扇形的半径 *参数5:顺时针还是逆时针,1表⽰顺时针,0表⽰逆时针(由于Core Graphics的坐标系和UIKit坐标系不同,我们实际看到的视图1表⽰逆时针,0表⽰顺时针) */ CGContextAddArc(tContext, center.x, center.y, radius, M_PI_2, M_PI, 1); [[UIColor blueColor] setFill]; [[UIColor cyanColor] setStroke]; CGContextClosePath(tContext); CGContextDrawPath(tContext,kCGPathFillStroke);
//绘制三次贝塞尔曲线 CGContextMoveToPoint(tContext, 200, 300); /*参数1:上下⽂ *参数2,3:曲线第⼀个控制点的x和y坐标 *参数4,5:曲线第⼆个控制点的x和y坐标 *参数6:路径的终点 */ CGContextAddCurveToPoint(tContext, 250, 250, 300, 350, 350, 300); [[UIColor whiteColor] setFill]; [[UIColor brownColor] setStroke]; CGContextClosePath(tContext); CGContextDrawPath(tContext,kCGPathFillStroke);
//绘制⼆次贝塞尔曲线 CGContextMoveToPoint(tContext, 50, 400); /*参数1:上下⽂ *参数2,3:曲线控制点的x和y坐标 *参数4:路径的终点 */ CGContextAddQuadCurveToPoint(tContext, 100, 450, 150, 400); CGFloat len[] = {5,4,3,6}; //设置路径宽度 CGContextSetLineWidth(tContext, 4); //设置路径虚线 CGContextSetLineDash(tContext, 0, len, 4); //设置路径颜⾊ [[UIColor redColor] setStroke]; // CGContextClosePath(tContext); CGContextDrawPath(tContext,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结构。CoreGraphics“对象”与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(tContext); //设置阴影的偏移量和模糊指数以及颜⾊(可以使⽤默认颜⾊,也可以使⽤⾃⼰定义的颜⾊) CGContextSetShadow(tContext, CGSizeMake(5, 10), 3); //CGContextSetShadowWithColor(tContext, CGSizeMake(5, 10), 5, [UIColor cyanColor].CGColor); //在这⾥绘制的图像会带有阴影效果 ...... //恢复没有阴影效果的状态 CGContextRestoreGState(tContext); //在这⾥绘制的图像没有带有阴影效果 ......效果图4-1如下所⽰:图渐变⽤来在图形中填充⼀系列平滑过渡的颜⾊。可以在CGGradientRef中设置需要的颜⾊和渐变的⽅式(线性渐变和径向渐变)。与填充颜⾊不同,⽆法⽤渐变填充路径,渐变会直接填满整个图形上下⽂,因此,如果需要将渐变应⽤在制定的范围内,必须使⽤剪切路径裁剪图形上下⽂。同时,与绘制阴影时的情况类似,没有函数可以删除剪切路径,同样需要在使⽤剪切路径前保存绘画状态,填充渐变之后再恢复状态。废话少说直接上代码: CGContextRef currentContext = UIGraphicsGetCurrentContext(); /* *渐变 */ //保存渐变之前的绘画状态 CGContextSaveGState(tContext);
//绘制渐变剪切路径 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(tContext, 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(tContext, gradient, CGPointMake(175, 550), 20, CGPointMake(175, 580), 60, 0);
//释放创建的C结构对象 CGGradientRelease(gradient); CGColorSpaceRelease(colorSpace); //恢复绘画状态 CGContextRestoreGState(tContext);效果图4-2如下:图源码已上传⾄fenglinyunshi-git,欢迎下载,并提出宝贵意见。结语Core Graphics是⼀个很⼤的课题,本篇从最基础的知识讲起,后续还会有陆续的篇章推出,敬请期待。未完待续...事在⼈为是⼀种积极的⼈⽣态度,顺其⾃然是⼀种达观的⽣存之道,⽔到渠成是⼀种⾼超的⼊世智慧,淡泊宁静是⼀种超脱的⽣活态度。
发布者:admin,转转请注明出处:http://www.yc00.com/web/1689263960a226398.html
评论列表(0条)