Core Graphics框架 利用Quartz 2D绘图

之前开发的项目中有“画板”功能模块,涉及到Quartz 2D绘图,于是在那一段时间研究学习了Core Graphics框架。今天用此文来总结梳理一下该框架各技术点。(由于技术点繁杂,本文借鉴博文:http://www.cnblogs.com/kenshincui/p/3959951.html 的目录结构和样例代码,结合自己的代码demo和理解来叙述)。既是总结归纳,也是帮助自己记忆梳理巩固细节。

 

首先,什么是Core Graphics和Quartz 2D?

Core Graphics:是基于Quartz 2D绘图引擎的一个C语言的API绘图框架。它也是iOS开发中最基本的框架(Framework)之一。两点原因,第一是该框架是每一个iOS应用最初被建立时,就被系统默认添加的三个框架(Foundation、UIKit、Core Graphics)之一;另一点是,我们平时常见的各种UIKit框架提供的UI控件,实际上都是由Core Graphics进行绘制的。

Quartz 2D:是一个绘图引擎,它被Core Graphics所封装所包含,它是Core Graphics的一部分。因此我们一般都是用CGxxxx命名的函数进行绘图。

 

其次,在哪里绘图?

在图形上下文对象中绘图,在Quartz 2D中的绘图上下文可以是位图Bitmap、PDF、窗口Window、层Layer、打印对象Printer。最常见的,我们在UIView和UIView子类上绘图,其实是在系统为我们已经准备好的一个图形上下文CGContextRef对象上绘图。这个CGContextRef对象只能在drawRect函数中获取,而且一定是自动调用的drawRect函数。(假如你自己手动调用该函数,将无法获取图形上下文CGContextRef对象,从而也就无法成功绘图。当然,这样做并不会造成app崩溃crash,或者其他致命错误,但是因为无法绘图,因此这样做是毫无意义的)。

 

绘图的步骤:(在drawRect函数中)

1.获取绘图上下文

2.创建并设置路径

3.将路径添加到上下文

4.设置上下文状态

5.绘制路径

6.释放路径

(这里借鉴了 http://www.cnblogs.com/kenshincui/p/3959951.html 的表述)

 

接下来,我将用一系列demo代码,来演示如何用Core Graphics的Quartz 2D绘图引擎绘出各种图形。(所有代码均书写在drawRect函数中,该drawRect函数位于我自定义的一个UIView子类中)

1,绘制线段

1.1,绘制一条直线

 1     // 获取绘图上下文
 2     CGContextRef context = UIGraphicsGetCurrentContext();
 3     
 4     // 设置起始点 设置另一端点
 5     CGContextMoveToPoint(context, 20, 50);
 6     CGContextAddLineToPoint(context, 50, 120);
 7     
 8     CGContextSetRGBStrokeColor(context, 1.0, 0, 0, 1);// 设置笔触颜色
 9     
10     // 绘制图像到指定图形上下文
11     CGContextDrawPath(context, kCGPathStroke);// 填充类型(仅边框)

效果:

可以看到,我们在空白的屏幕上,绘制了一条红色的线段。从代码可以看出,我并没有完全使用之前说的“绘图六步骤”,原因是Core Graphics对绘图做了很友好的封装,我们可以在获取到绘图上下文后,直接设置端点和移动到的另一个端点,设置颜色属性,即可调用绘图方法完成绘图,这是一种简便方法。

1.2,绘制一系列线段,设置线段颜色、填充色、端点样式、线段样式、阴影等属性

 1     // 获取绘图上下文
 2     CGContextRef context = UIGraphicsGetCurrentContext();
 3     
 4     // 设置起始点 设置多条线段
 5     CGContextMoveToPoint(context, 20, 50);
 6     CGContextAddLineToPoint(context, 20, 100);
 7     CGContextAddLineToPoint(context, 300, 100);
 8     CGContextAddLineToPoint(context, 300, 300);
 9     CGContextAddLineToPoint(context, 100, 300);
10     
11     // 设置图形上下文状态属性
12     CGContextSetRGBStrokeColor(context, 1.0, 0, 0, 1);// 设置笔触颜色
13     CGContextSetRGBFillColor(context, 0, 1.0, 0, 1);// 设置填充色
14     CGContextSetLineWidth(context, 1.0);// 设置线条宽度
15     CGContextSetLineCap(context, kCGLineCapRound);// 设置顶点样式,首尾两个端点是顶点
16     CGContextSetLineJoin(context, kCGLineJoinRound);// 设置连接点样式,除了首尾两点之外的中间端点都是是连接点
17     
18     // 若设置此项,则将会把线段设置为虚线,虚线的“格式”为一段10像素长的线段加一段5像素长的空白,依次循环。
19     // 另外,可以通过对数组定义多个元素来达到多种虚线段样式组合。
20     CGFloat lengths[2] = {10, 5};
21     CGContextSetLineDash(context, 0, lengths, 2);// 2 是元素个数
22     
23     // 若设置此项,则将给绘出的图形设置阴影
24     // 在Quartz 2D中,要使用CGColor方法将UIColor转化为CGColorRef
25     CGColorRef color = [UIColor grayColor].CGColor;
26     // offset:偏移量 blur:模糊度 color:阴影颜色
27     CGContextSetShadowWithColor(context, CGSizeMake(2, 2), 0.8, color);
28     
29     // 绘制图像到指定图形上下文
30     CGContextDrawPath(context, kCGPathFillStroke);// 填充类型(有边框,有填充)

 效果:

代码中都做了详细注释,除此之外值得一提的两点:

一是填充绿颜色的这块区域是这样得到确认的:如果我们绘图时没有显示的去连接起点和终点,那么在绘图模式中如果选择了“fill(填充)”相关的模式,绘图引擎会默认将首尾端点“视作连接”,然后对线段所包围的区域进行颜色填充。(有过看过大众点评客户端,有一个画圈找商家的功能,任凭你画了一个什么奇怪的“圈圈”,他都能帮你标出一块你画的圈包围的区域,用的就是这里的绘图原理)。

二是绘图模式,此处用的是边框和填充同时绘图,另外还有只会边框、只绘填充区域、路径交叉时按奇偶规则进行填充或绘边框,效果如下:

                              

 

2,绘制矩形

绘制矩形有两种方法,2.1展现的是使用CG方法;2.2展现的是使用更高级的UI封装的方法。

2.1,Core Graphics方法:

 1     // 获取绘图上下文
 2     CGContextRef context = UIGraphicsGetCurrentContext();
 3     
 4     // 添加矩形对象
 5     CGRect myRect = CGRectMake(20, 50, 280.0, 50.0);
 6     CGContextAddRect(context, myRect);
 7     
 8     // 设置属性
 9     [[UIColor blueColor] setFill];
10     [[UIColor redColor] setStroke];
11     
12     // 绘制
13     CGContextDrawPath(context, kCGPathFillStroke);

调用CGContextAddRect方法,该方法创建并添加了一个矩形路径到绘图上下文对象。设置响应的边框颜色,填充颜色。最后边框和填充均绘制。

 2.2,UI方法

1     CGRect myRect2= CGRectMake(20, 150, 280.0, 50.0);
2     // 设置属性
3     [[UIColor greenColor] set];
4     // 直接使用UIRectFill绘制矩形
5     UIRectFrame(myRect2);// 绘制矩形(填充模式)

这种UIRectFrame,无需显示地获取绘图上下文对象CGContextRef,因为该方法会自动获取图形上下文,并自觉地在图形上下文上绘图。但是,你不能在drawRect函数外调用这个函数进行绘图,因为,只有在系统自动调用的drawRect函数中,我们才能拿到绘图上下文。

另外,我们还可以利用UIColor的 setFill、setStroke、set这三个方法简便地设置边框、填充的颜色,这也利用了UI封装的高级方法。

效果如下:

3,绘制椭圆形(圆形)

 1     // 添加对象,绘制椭圆(圆形)的过程也是先创建一个矩形
 2     CGRect myRect=CGRectMake(50, 50, 220.0, 200.0);
 3     CGContextAddEllipseInRect(context, myRect);
 4     //设置属性
 5     [[UIColor blueColor] set];
 6     CGColorRef color = [UIColor grayColor].CGColor;
 7     // offset:偏移量 blur:模糊度 color:阴影颜色
 8     CGContextSetShadowWithColor(context, CGSizeMake(3, 3), 0.8, color);
 9     //绘制
10     CGContextDrawPath(context, kCGPathFill);

绝大多数的关键点都不用重复解释了,大家可以发现,其实就是调用相应的CG函数,创建并添加路径,设置好属性,绘图即可。

效果:

4,绘制弧形

 1     // 获取绘图上下文
 2     CGContextRef context = UIGraphicsGetCurrentContext();
 3     
 4     // 从左到右除了绘图上下文以外的参数说明
 5     // x:中心点x坐标
 6     // y:中心点y坐标
 7     // radius:半径
 8     // startAngle:起始弧度
 9     // endAngle:终止弧度
10     // closewise:是否逆时针绘制,1则顺时针绘制,0则逆时针绘制
11     CGContextAddArc(context, 160, 160, 100.0, 0.0, 1.5*M_PI, 0);
12     
13     //设置属性
14     [[UIColor blueColor] set];
15     
16     //绘制
17     CGContextDrawPath(context, kCGPathStroke);

绘制弧形,同样也是调用响应的CG函数。请注意每个参数的意义。(http://www.cnblogs.com/kenshincui/p/3959951.html 这里有一个错误,作者将CGContextAddArc函数最后一个参数的顺时针逆时针弄反了,因此请看我的代码中的正确注释)

效果:

这是一个 1.5倍的π的弧,请注意起始弧度0.0度在二维坐标的x轴正向。

5,绘制贝赛尔曲线

 1     // 获取绘图上下文
 2     CGContextRef context = UIGraphicsGetCurrentContext();
 3     
 4     // 初始位置端点
 5     CGContextMoveToPoint(context, 20, 100);
 6     
 7     // 二次贝塞尔曲线
 8     // cpx:控制点x坐标
 9     // cpy:控制点y坐标
10     // 结束点x坐标
11     // 结束点y坐标
12     CGContextAddQuadCurveToPoint(context, 160, 0, 300, 100);
13     
14     CGContextMoveToPoint(context, 20, 400);
15     
16     // 三次贝塞尔曲线
17     // cp1x:第一个控制点x坐标
18     // cp1y:第一个控制点y坐标
19     // cp2x:第二个控制点x坐标
20     // cp2y:第二个控制点y坐标
21     // x:结束点x坐标
22     // y:结束点y坐标
23     CGContextAddCurveToPoint(context, 80, 300, 240, 500, 300, 300);
24     
25     //设置图形上下文属性
26     [[UIColor blueColor]setFill];
27     [[UIColor redColor]setStroke];
28     
29     //绘制路径
30     CGContextDrawPath(context, kCGPathFillStroke);

贝赛尔曲线的原理我在这里就不详细讲解了,读者自己百科。简单说一下就是二次贝赛尔曲线,需要三个点,分别是起点、终点、控制点;而三次贝赛尔曲线则比二次贝塞尔曲线多一个控制点。而且当三次贝塞尔曲线的两个控制点都位于首尾两个端点的连线的一侧时,就转化为二次贝塞尔曲线了。

效果:

6,绘制文字

 1     // 绘制的文本内容
 2     NSString *str=@"Quartz 2D is an advanced, two-dimensional drawing engine available for iOS application development and to all Mac OS X application environments outside of the kernel. Quartz 2D provides low-level, lightweight 2D rendering with unmatched output fidelity regardless of display or printing device. Quartz 2D is resolution- and device-independent; you don’t need to think about the final destination when you use the Quartz 2D application programming interface (API) for drawing.";
 3     CGRect myRect= CGRectMake(20, 50, 280, 300);
 4     // 设置字体
 5     UIFont *font=[UIFont systemFontOfSize:18];
 6     // 字体颜色
 7     UIColor *color=[UIColor blueColor];
 8     // 段落样式
 9     NSMutableParagraphStyle *style=[[NSMutableParagraphStyle alloc]init];
10     // 对齐方式
11     style.alignment=NSTextAlignmentLeft;
12     [str drawInRect:myRect withAttributes:@{NSFontAttributeName:font,NSForegroundColorAttributeName:color,NSParagraphStyleAttributeName:style}];

使用drawInRect方法绘制文本,效果:

7,绘制图像

1     UIImage *image=[UIImage imageNamed:@"1.png"];
2     // 从某一点开始绘制
3 //    [image drawAtPoint:CGPointMake(10, 50)];
4     // 绘制到指定的矩形中,注意如果大小不合适会会进行拉伸
5     [image drawInRect:CGRectMake(10, 50, 300, 400)];
6     // 平铺绘制
7 //    [image drawAsPatternInRect:CGRectMake(0, 0, 320, 568)];

利用drawAtPoint、drawInRect、drawAsPatternInRect方法进行绘制图像。

效果依次分别如下:

                    

8,绘制渐变

所谓渐变,我的理解就是不同颜色间的过度。那么,需要的要素无外乎就是这么几个:不同的颜色、各个颜色所占的“比例”、绘制的位置,以及渐变的形式。但是在此之前,有一点需要提醒一下,绘制渐变需要事先指定颜色空间。比如我们这里代码中的CGColorSpaceCreateDeviceRGB(),使用的就是RGB颜色空间。

另外,渐变有两种形式,一种是线性渐变,另一种是径向渐变。

8.1,线性渐变

 1     // 获取绘图上下文
 2     CGContextRef context = UIGraphicsGetCurrentContext();
 3     
 4     //使用rgb颜色空间
 5     CGColorSpaceRef colorSpace=CGColorSpaceCreateDeviceRGB();
 6     
 7     // compoents:该数组内包含的是RGB值构成的颜色数据,由于RGB值需要四个参数(red、green、blue、alpha),所以假如有三个颜色,则components应该有4*3=12个元素
 8     CGFloat compoents[12]={
 9         248.0/255.0,86.0/255.0,86.0/255.0,1,
10         249.0/255.0,127.0/255.0,127.0/255.0,1,
11         1.0,1.0,1.0,1.0
12     };
13     // locations:颜色所在位置(范围0~1),这个数组的个数不小于components中存放颜色的个数
14     CGFloat locations[3]={0,0.3,1.0};
15     // count:渐变个数,等于locations的个数
16     // CGGradientCreateWithColorComponents 设置渐变各项参数 获得CGGradientRef对象
17     CGGradientRef gradient= CGGradientCreateWithColorComponents(colorSpace, compoents, locations, 3);
18     
19     // startPoint:起始位置
20     // endPoint:终止位置
21     // options:绘制方式,kCGGradientDrawsBeforeStartLocation 开始位置之前就进行绘制,到结束位置之后不再绘制,kCGGradientDrawsAfterEndLocation开始位置之前不进行绘制,到结束点之后继续填充
22     CGContextDrawLinearGradient(context, gradient, CGPointZero, CGPointMake(320, 300), kCGGradientDrawsAfterEndLocation);
23     
24     //释放颜色空间
25     CGColorSpaceRelease(colorSpace);

效果:

8.2,径向渐变

 1     // 获取绘图上下文
 2     CGContextRef context = UIGraphicsGetCurrentContext();
 3     
 4     // 使用rgb颜色空间
 5     CGColorSpaceRef colorSpace=CGColorSpaceCreateDeviceRGB();
 6     
 7     // 同线性渐变
 8     CGFloat compoents[12]={
 9         248.0/255.0,86.0/255.0,86.0/255.0,1,
10         249.0/255.0,127.0/255.0,127.0/255.0,1,
11         1.0,1.0,1.0,1.0
12     };
13     CGFloat locations[3]={0,0.3,1.0};
14     CGGradientRef gradient= CGGradientCreateWithColorComponents(colorSpace, compoents, locations, 3);
15     
16     // startCenter:起始点位置
17     // startRadius:起始半径(通常为0,否则在此半径范围内容无任何填充)
18     // endCenter:终点位置(通常和起始点相同,否则会有偏移)
19     // endRadius:终点半径(也就是渐变的扩散长度)
20     // options:绘制方式,kCGGradientDrawsBeforeStartLocation 开始位置之前就进行绘制,但是到结束位置之后不再绘制,kCGGradientDrawsAfterEndLocation开始位置之前不进行绘制,但到结束点之后继续填充
21     CGContextDrawRadialGradient(context, gradient, CGPointMake(160, 284), 0, CGPointMake(165, 289), 150, kCGGradientDrawsAfterEndLocation);
22     //释放颜色空间
23     CGColorSpaceRelease(colorSpace);

效果:

假如起始半径设置为“10”,则效果是圆心被“挖去”:

9,绘制填充图案

有时候,我们需要在“画布”上绘制一系列有规则的重复图案,就像 http://www.cnblogs.com/kenshincui/p/3959951.html 里说的“贴瓷砖”。(引用)“Quartz 2D支持两种绘制填充图案:“有颜色填充”和“无颜色填充”。两种模式使用起来区别很小,有颜色填充就是在绘制瓷砖(即“单位图形” 引用者注,下同)时就指定颜色,在调用填充时就不用再指定瓷砖颜色;无颜色填充模式就是绘制瓷砖时不用指定任何颜色,在调用填充时再指定具体填充颜色。相比较无颜色填充模式而言,有颜色填充模式更加的灵活,推荐使用。”

我们分别来用两种方式绘制下面一种图案:

9.1,有颜色填充图案

 1     // 获取绘图上下文
 2     CGContextRef context = UIGraphicsGetCurrentContext();
 3     
 4     // 创建填充的颜色空间 有颜色填充 传NULL
 5     CGColorSpaceRef colorSpace = CGColorSpaceCreatePattern(NULL);
 6     // 设置颜色空间
 7     CGContextSetFillColorSpace(context, colorSpace);
 8     
 9     // 设置实际创建单位图形的回调函数
10     CGPatternCallbacks callback={0, &drawColoredTile, NULL};
11     
12     // 创建单位图形 参数依次为:
13     // info:传递给callback的参数
14     // bounds:单位图形绘制大小
15     // matrix:形变
16     // xStep:单位图形分配宽度
17     // yStep:单位图形分配高度
18     // tiling:三种模式,控制是否扭曲
19     // isClored:绘制的瓷砖是否指定颜色(对于有颜色瓷砖此处指定位true)
20     // callbacks:设置回调函数
21     CGPatternRef pattern=CGPatternCreate(NULL, CGRectMake(0, 0, 2*TILE_SIZE, 2*TILE_SIZE), CGAffineTransformIdentity, 2*TILE_SIZE,2*TILE_SIZE, kCGPatternTilingNoDistortion, true, &callback);
22     
23     CGFloat alpha = 1.0;
24     // 注意最后一个参数对于有颜色瓷砖指定为透明度的参数地址,对于无颜色瓷砖则指定当前颜色空间对应的颜色数组
25     CGContextSetFillPattern(context, pattern, &alpha);
26     
27     UIRectFill(CGRectMake(0, 0, 320, 480));
28     
29     // 释放
30     CGColorSpaceRelease(colorSpace);
31     CGPatternRelease(pattern);
32     
 1 void drawColoredTile(void *info,CGContextRef context){
 2     // 有颜色填充,在这里设置填充色
 3     CGContextSetRGBFillColor(context, 254.0/255.0, 52.0/255.0, 90.0/255.0, 1);
 4     CGContextFillRect(context, CGRectMake(0, 0, TILE_SIZE, TILE_SIZE));
 5     CGContextFillRect(context, CGRectMake(TILE_SIZE, TILE_SIZE, TILE_SIZE, TILE_SIZE));
 6     
 7     CGContextSetRGBFillColor(context, 0.0/255.0, 160.0/255.0, 190.0/255.0, 1);
 8     CGContextFillRect(context, CGRectMake(TILE_SIZE, 0, TILE_SIZE, TILE_SIZE));
 9     CGContextFillRect(context, CGRectMake(0, TILE_SIZE, TILE_SIZE, TILE_SIZE));
10 }

效果:

注意点:1,创建填充的颜色空间 有颜色填充 这里的参数要传“NULL” CGColorSpaceRef colorSpace = CGColorSpaceCreatePattern(NULL);2,回调drawColoredTile是由Core Graphics内部调用的,因此只能使用CG方法绘图,不要使用UI封装方法,如果使用UI方法,是无法绘出图的;3,凡是Create方法获取的对象,一定要调用响应的release方法。

9.2,无颜色填充图案

 1     // 获取绘图上下文
 2     CGContextRef context = UIGraphicsGetCurrentContext();
 3     
 4     // 创建RGB颜色空间
 5     CGColorSpaceRef rgbSpace= CGColorSpaceCreateDeviceRGB();
 6     // 使用RGB颜色空间创建填充颜色空间
 7     CGColorSpaceRef colorSpace=CGColorSpaceCreatePattern(rgbSpace);
 8     // 将填充色颜色空间设置为模式填充的颜色空间
 9     CGContextSetFillColorSpace(context, colorSpace);
10     
11     //填充模式回调函数结构体
12     CGPatternCallbacks callback={0, &drawTile, NULL};
13     
14     // 创建单位图形 参数依次为:
15     // info:传递给callback的参数
16     // bounds:单位图形绘制大小
17     // matrix:形变
18     // xStep:单位图形分配宽度
19     // yStep:单位图形分配高度
20     // tiling:三种模式,控制是否扭曲
21     // isClored:绘制的瓷砖是否指定颜色(对于无颜色瓷砖此处指定位false)
22     // callbacks:设置回调函数
23     CGPatternRef pattern=CGPatternCreate(NULL, CGRectMake(0, 0, 2*TILE_SIZE, 2*TILE_SIZE), CGAffineTransformIdentity,2*TILE_SIZE,2*TILE_SIZE, kCGPatternTilingNoDistortion, false, &callback);
24     
25     CGFloat components[]={254.0/255.0,52.0/255.0,90.0/255.0,1.0};
26     // 注意最后一个参数对于无颜色填充模式指定为当前颜色空间颜色数据
27     CGContextSetFillPattern(context, pattern, components);
28     UIRectFill(CGRectMake(0, 0, 320, 568));
29     
30     CGColorSpaceRelease(rgbSpace);
31     CGColorSpaceRelease(colorSpace);
32     CGPatternRelease(pattern);
1 void drawTile(void *info,CGContextRef context){
2     CGContextFillRect(context, CGRectMake(0, 0, TILE_SIZE, TILE_SIZE));
3     CGContextFillRect(context, CGRectMake(TILE_SIZE, TILE_SIZE, TILE_SIZE, TILE_SIZE));
4 }

可以看出,9.2运用的“无颜色填充”方式,在灵活性上比9.1“有颜色填充”要差很多。

10,图形上下文的变换

有的时候我们需要对图像上下文进行整体“变换”,这些“变换”包括了旋转,移动和缩放等。比如下面的例子,分别进行了上述三种变换:

 1     // 获取绘图上下文
 2     CGContextRef context = UIGraphicsGetCurrentContext();
 3     
 4     // 保存初始状态
 5     CGContextSaveGState(context);
 6     // 形变第一步:图形上下文向右平移80
 7     CGContextTranslateCTM(context, 80, 0);
 8     // 形变第二步:缩放0.8
 9     CGContextScaleCTM(context, 0.8, 0.8);
10     // 形变第三步:旋转
11     CGContextRotateCTM(context, M_PI_4/4);
12     
13     UIImage *image=[UIImage imageNamed:@"1.png"];
14     [image drawInRect:CGRectMake(0, 50, 240, 300)];
15     
16     //恢复到初始状态
17     CGContextRestoreGState(context);

得到的效果如下:

上面的截图是三种变换共同的效果,这里有必要解释一下CGContextSaveGState(context);和CGContextRestoreGState(context);这一对方法的作用是,前者的调用,将会把当前的上下文状态保存在一个“绘图状态栈”中,后者的作用是,将上一次保存在栈中的上下文状态取出,作为当前的上下文状态。这样做的目的,是因为对上下文进行变换会改变上下文整体坐标系,如果在变换上下文前,不进行保存,之后也不恢复的话,再在此上下文绘图,就会按照新的变化后的坐标系绘图,从而发生混乱。

11,使用CG方法绘制图片

在“7,绘制图像”中,我使用的绘图方式是UIImage中的封装的UI方法,其实CG方法也可以绘图,不过要注意,这里的使用CG方法在绘图上下文中绘制,坐标是颠倒相反的。因为“在Core Graphics中坐标系的y轴正方向是向上的,坐标原点在屏幕左下角,y轴方向刚好和UIKit中y轴方向相反”。但是我又发现在Core Graphics中绘制其他图形(非UI方法)没有颠倒的问题,这是因为只有在Core Graphics中用CG方法绘制图像,才会有颠倒的问题!因此正确的绘制图片的方法如下:

 1     // 获取绘图上下文
 2     CGContextRef context = UIGraphicsGetCurrentContext();
 3     
 4     // 拿到图片
 5     UIImage *image=[UIImage imageNamed:@"1.png"];
 6     
 7     // 保存上下文状态
 8     CGContextSaveGState(context);
 9     
10     // 设置绘图宽高
11     CGFloat height = 400;
12     CGFloat width = 300;
13     
14     // 在y轴缩放-1相当于沿着x张旋转180 注意!此时x轴不变,y轴因旋转而正向变为朝上,原点不变仍然在左上角
15     CGContextScaleCTM(context, 1.0, -1.0);
16     // 向下平移一个图片高度
17     CGContextTranslateCTM(context, 0, -height);
18     
19     //图像绘制
20     CGRect myRect= CGRectMake(0, 0, width, height);
21     CGContextDrawImage(context, myRect, image.CGImage);
22     
23     // 恢复上下文状态
24     CGContextRestoreGState(context);

效果:

由此可见其实http://www.cnblogs.com/kenshincui/p/3959951.html中的相关表述是有问题的,而我认为我的代码注释才是正确的。这是我找到的原博文的第二个bug。

12,关于重新绘制UIView及其子类

之前说了drawRect函数是系统默认调用的,我们自己调用将起不到效果,因为自己调用是无法获得绘图上下文的。那么假如我想重新绘图怎么办呢,可以调用setNeedsDisplay函数,系统会自行调用drawRect函数。这样,你就可以实现“重绘”了。

13,绘制到位图

前面12节讲述的都是在drawRect方法中绘制,用到的是系统为我们在该函数中准备好的绘图上下文,绘图实际上是绘到了UIView的layer层上。其实,更灵活的是,我们也可以在其他类型,例如位图的图形上下文绘图。而且此时,由于系统不会自动为我们创建图形上下文,我们就需要自己创建“位图图形上下文”。

 1 -(UIImage *)drawImageAtImageContext{
 2     // 自己创建一个位图图形上下文
 3     CGSize size = CGSizeMake(300, 400);
 4     UIGraphicsBeginImageContext(size);
 5     
 6     UIImage *image = [UIImage imageNamed:@"1.png"];
 7     [image drawInRect:CGRectMake(0, 0, 300, 400)];//注意绘图的位置是相对于画布顶点而言,不是屏幕
 8     
 9     // 绘制线段图形
10     CGContextRef context = UIGraphicsGetCurrentContext();
11     CGContextMoveToPoint(context, 20, 50);
12     CGContextAddLineToPoint(context, 180, 50);
13     CGContextSetLineWidth(context, 2);
14     [[UIColor redColor] setStroke];
15     
16     CGContextDrawPath(context, kCGPathStroke);
17     
18     // 绘制文字
19     NSString *str = @"pigpigdaddy";
20     [str drawInRect:CGRectMake(20, 20, 180, 60) withAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:20],NSForegroundColorAttributeName:[UIColor redColor]}];
21     
22     // 获取绘制后的新图形
23     UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
24     
25     // 关闭对应的上下文
26     UIGraphicsEndImageContext();
27     
28     return newImage;
29 }
 1 - (id)initWithFrame:(CGRect)frame
 2 {
 3     self = [super initWithFrame:frame];
 4     if (self) {
 5         // Initialization code
 6         self.backgroundColor = [UIColor whiteColor];
 7         
 8         UIImage *image=[self drawImageAtImageContext];
 9         UIImageView *imageView=[[UIImageView alloc]initWithImage:image];
10         imageView.center = self.center;
11         
12         [self addSubview:imageView];
13         
14     }
15     return self;
16 }

效果:

总结:

本文用了13个例子简单复习了Core Graphics中的各种图形的绘图。很多我认为的注意点都写在了上面。同时,鉴了很多博客的结构、例子、表述,也发现了他们的很多bug笔误,都做了更正。

参考文章推荐大家去看一下:

http://www.cnblogs.com/kenshincui/p/3959951.html

http://www.cocoachina.com/ios/20111111/3486.html

 

posted @ 2014-09-24 10:44  pigpigdaddy  阅读(778)  评论(0编辑  收藏  举报