当前位置:首页 > 通信资讯 > 正文

一、前言

quartz2d的api是纯c语言的,它是一个二维绘图引擎,同时支持ios和mac系统。quartz2d的api来自于core graphics框架,数据类型和函数基本都以cg作为前缀。通常,我们可以使用系统提供的控件去完成大部分ui,但是有些ui界面极其复杂、而且比较个性化,用普通的ui控件无法实现,这时可以利用quartz2d技术将控件内部的结构画出来,类似自定义控件。其实,ios中大部分控件的内容都是通过quartz2d画出来的,因此,quartz2d在ios开发中很重要的一个价值是:自定义view(自定义ui控件)。

quartz 2d能完成的工作:

  1. 绘制图形 : 线条三角形矩形圆弧等;
  2. 绘制文字;
  3. 绘制生成图片(图像);
  4. 读取生成pdf;
  5. 截图裁剪图片;
  6. 自定义ui控件;
  7. … …

二、图形上下文(graphics context)

图形上下文(graphics context):是一个cgcontextref类型的数据。

图形上下文的作用:

(1)保存绘图信息、绘图状态

(2)决定绘制的输出目标(绘制到什么地方去?)

(输出目标可以是pdf文件、bitmap或者显示器的窗口上)

ios开发之quartz2d的介绍与使用详解(ios开发之quartz2d的介绍与使用详解)

相同的一套绘图序列,指定不同的graphics context,就可将相同的图像绘制到不同的目标上。

quartz2d提供了以下几种类型的graphics context:

(1)bitmap graphics context

(2)pdf graphics context

(3)window graphics context

(4)layer graphics context

(5)printer graphics context

ios开发之quartz2d的介绍与使用详解(ios开发之quartz2d的介绍与使用详解)

将当前的上下文copy一份,保存到栈顶(那个栈叫做”图形上下文栈”):

?
1 void cgcontextsavegstate(cgcontextref c)

将栈顶的上下文出栈,替换掉当前的上下文(清空之前对于上下文设置):

?
1 void cgcontextrestoregstate(cgcontextref c)

三、使用quartz2d自定义view

1、quartz2d自定义view

如何利用quartz2d自定义view?(自定义ui控件)如何利用quartz2d绘制东西到view上?

首先,得有图形上下文,因为它能保存绘图信息,并且决定着绘制到什么地方去。

其次,那个图形上下文必须跟view相关联,才能将内容绘制到view上面。

自定义view的步骤:

(1)新建一个类,继承自uiview

(2)实现- (void)drawrect:(cgrect)rect方法,然后在这个方法中

(a)取得跟当前view相关联的图形上下文

?
1 cgcontextref ctx = uigraphicsgetcurrentcontext();

(b)绘制相应的图形内容

例如:画1/4圆(扇形)

?
1 2 3 4 5 6 7 8 9 10 cgcontextmovetopoint(ctx, 100, 100); cgcontextaddlinetopoint(ctx, 100, 150); cgcontextaddarc(ctx, 100, 100, 50, -m_pi_2, m_pi, 1); cgcontextclosepath(ctx); [[uicolor redcolor] set];

(3)利用图形上下文将绘制的所有内容渲染显示到view上面

?
1 cgcontextfillpath(ctx);

注:

?
1 2 3 4 5 6 7 8 9 10 11 12 13 //m_pi的含义:π //m_pi * 2的含义:2π //m_pi_2的含义:π/2 //m_pi / 2的含义:π/2 // 画的图形路径 //bezierpathwitharccenter:弧所在的圆心 //radius:圆的半径 //startangle:开始角度,圆的最右侧为0度 //endangle:截至角度,向下为正,向上为负. //clockwise:时针的方向,yes:顺时针 no:逆时针 uibezierpath *path = [uibezierpath bezierpathwitharccenter:self.center radius:radius startangle:starta endangle:enda clockwise:no];

完整代码:

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 // // myview.m // quartz2dtest // // created by 李峰峰 on 2017/2/6. // copyright © 2017年 李峰峰. all rights reserved. // #import "myview.h" @implementation myview - (instancetype)initwithframe:(cgrect)frame { self = [super initwithframe:frame]; if (self) { self.backgroundcolor = [uicolor whitecolor];//设置背景为白色,为了便于观察绘制后的效果 } return self; } - (void)drawrect:(cgrect)rect { cgcontextref ctx = uigraphicsgetcurrentcontext(); cgcontextmovetopoint(ctx, 100, 100); cgcontextaddlinetopoint(ctx, 100, 150); cgcontextaddarc(ctx, 100, 100, 50, -m_pi_2, m_pi, 1); cgcontextclosepath(ctx); [[uicolor redcolor] set]; cgcontextfillpath(ctx); } @end

运行效果:

ios开发之quartz2d的介绍与使用详解(ios开发之quartz2d的介绍与使用详解)

2、核心方法drawrect:

为什么要实现drawrect:方法才能绘图到view上?

因为在drawrect:方法中才能取得跟view相关联的图形上下文

drawrect:方法在什么时候被调用?

当view第一次显示到屏幕上时(被加到uiwindow上显示出来)

调用view的setneedsdisplay或者setneedsdisplayinrect:时.

注意4点:

  1. 手动调用drawrect:方法,不会自动创建跟view相关联的上下文。应该调用setneedsdisplay方法,系统底层会自动调用drawrect,告诉系统重新绘制view.这样,系统底层会自动创建跟view相关联的上下文
  2. setneedsdisplay底层会调用drawrect,并不是立马调用的.只是设了一个调用的标志.调用时刻是等下一次屏幕刷新时才去调用drawrect。屏幕每一秒刷新30-60秒次,所以1秒调用drawrect方法大概30-60次,速度非常快哦
  3. view内部有个layer(图层)属性,drawrect:方法中取得的是一个layer graphics context,因此,绘制的东西其实是绘制到view的layer上去了
  4. view之所以能显示东西,完全是因为它内部的layer

3、quartz2d绘图的代码步骤

第一步:获得图形上下文:

?
1 cgcontextref ctx = uigraphicsgetcurrentcontext();

第二步:拼接路径(下面代码是绘制一条线段):

?
1 2 cgcontextmovetopoint(ctx, 10, 10); cgcontextaddlinetopoint(ctx, 100, 100);

第三步:绘制路径:

?
1 cgcontextstrokepath(ctx); // cgcontextfillpath(ctx);

四、quartz2d重要函数

1、常用拼接路径函数

新建一个起点

?
1 void cgcontextmovetopoint(cgcontextref c, cgfloat x, cgfloat y)

添加新的线段到某个点

?
1 void cgcontextaddlinetopoint(cgcontextref c, cgfloat x, cgfloat y)

添加一个矩形

?
1 void cgcontextaddrect(cgcontextref c, cgrect rect)

添加一个椭圆

?
1 void cgcontextaddellipseinrect(cgcontextref context, cgrect rect)

添加一个圆弧

?
1 2 3 void cgcontextaddarc(cgcontextref c, cgfloat x, cgfloat y, cgfloat radius, cgfloat startangle, cgfloat endangle, int clockwise)

2、常用绘制路径函数

一般以cgcontextdraw、cgcontextstroke、cgcontextfill开头的函数,都是用来绘制路径的。

mode参数决定绘制的模式

?
1 void cgcontextdrawpath(cgcontextref c, cgpathdrawingmode mode)

绘制空心路径

?
1 void cgcontextstrokepath(cgcontextref c)

绘制实心路径

?
1 void cgcontextfillpath(cgcontextref c)

3、矩阵操作函数

利用矩阵操作,能让绘制到上下文中的所有路径一起发生变化。

缩放:

?
1 void cgcontextscalectm(cgcontextref c, cgfloat sx, cgfloat sy)

旋转:

?
1 void cgcontextrotatectm(cgcontextref c, cgfloat angle)

平移:

?
1 void cgcontexttranslatectm(cgcontextref c, cgfloat tx, cgfloat ty)

4、其他常用函数

设置线段宽度

?
1 cgcontextsetlinewidth(ctx, 10);

设置线段头尾部的样式

?
1 cgcontextsetlinecap(ctx, kcglinecapround);

设置线段转折点的样式

?
1 cgcontextsetlinejoin(ctx, kcglinejoinround);

设置颜色

?
1 cgcontextsetrgbstrokecolor(ctx, 1, 0, 0, 1);

五、quartz2d的内存管理

关于quartz2d内存管理,有以下原则:

(1)使用含有“create”或“copy”的函数创建的对象,使用完后必须释放,否则将导致内存泄露。

(2)使用不含有“create”或“copy”的函数获取的对象,则不需要释放

(3)如果retain了一个对象,不再使用时,需要将其release掉。

(4)可以使用quartz 2d的函数来指定retain和release一个对象。例如,如果创建了一个cgcolorspace对象,则使用函数cgcolorspaceretain和cgcolorspacerelease来retain和release对象。

(5)也可以使用core foundation的cfretain和cfrelease。注意不能传递null值给这些函数。

六、quartz2d使用案例

1、画矩形、正方形

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 - (void)drawrect:(cgrect)rect { //1.获取上下文 cgcontextref ctx = uigraphicsgetcurrentcontext(); //2.描述路径 uibezierpath *path = [uibezierpath bezierpathwithrect:cgrectmake(50, 50, 200, 200)]; //3.把路径添加到上下文 cgcontextaddpath(ctx, path.cgpath); [[uicolor redcolor] set];// 路径的颜色 //4.把上下文的内容渲染到view的layer. //cgcontextstrokepath(ctx);// 描边路径 cgcontextfillpath(ctx);// 填充路径 }

运行效果:

ios开发之quartz2d的介绍与使用详解(ios开发之quartz2d的介绍与使用详解)

2、画扇形

除上面“二、使用quartz2d自定义view”中的方法外,也可以使用oc中自带画图方法实现,如下:

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 - (void)drawrect:(cgrect)rect { cgpoint center = cgpointmake(rect.size.width * 0.5, rect.size.height * 0.5); cgfloat radius = rect.size.width * 0.5 - 10; cgfloat starta = 0; cgfloat enda = -m_pi_2; // 画弧的路径 uibezierpath *path = [uibezierpath bezierpathwitharccenter:center radius:radius startangle:starta endangle:enda clockwise:no]; // 添加一根线到圆心 [path addlinetopoint:center]; // 闭合路径 [path closepath]; // 路径颜色 [[uicolor redcolor] set]; // 填充路径 [path fill]; // 描边路径 //[path stroke]; }

运行效果:

ios开发之quartz2d的介绍与使用详解(ios开发之quartz2d的介绍与使用详解)

注:

判断一个点是否在一个矩形框内

?
1 cgrectcontainspoint(rect,point);//判断point这个点是否在rect这个矩形框内

3、画圆形

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 - (void)drawrect:(cgrect)rect { //1.获取上下文 cgcontextref ctx = uigraphicsgetcurrentcontext(); //2.描述路径 // cornerradius:圆角半径。矩形的宽高都为200,如果圆角为100,那么两个角之间弧线上任意一点到矩形中心的距离都为100,所以为圆形 uibezierpath *path = [uibezierpath bezierpathwithroundedrect:cgrectmake(50, 50, 200, 200) cornerradius:100]; //3.把路径添加到上下文 cgcontextaddpath(ctx, path.cgpath); [[uicolor redcolor] set];// 路径的颜色 //4.把上下文的内容渲染到view的layer. // cgcontextstrokepath(ctx);// 描边路径 cgcontextfillpath(ctx);// 填充路径 }

运行效果:

ios开发之quartz2d的介绍与使用详解(ios开发之quartz2d的介绍与使用详解)

4、画圆角矩形

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 - (void)drawrect:(cgrect)rect { //1.获取上下文 cgcontextref ctx = uigraphicsgetcurrentcontext(); //2.描述路径 // cornerradius:圆角半径。 uibezierpath *path = [uibezierpath bezierpathwithroundedrect:cgrectmake(50, 50, 200, 200) cornerradius:50]; //3.把路径添加到上下文 cgcontextaddpath(ctx, path.cgpath); [[uicolor redcolor] set];// 路径的颜色 //4.把上下文的内容渲染到view的layer. cgcontextstrokepath(ctx);// 描边路径 //cgcontextfillpath(ctx);// 填充路径 }

运行效果:

ios开发之quartz2d的介绍与使用详解(ios开发之quartz2d的介绍与使用详解)

5、画直线

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 - (void)drawrect:(cgrect)rect { //1.获取跟view相关联的上下文(uigraphics开头) cgcontextref ctx = uigraphicsgetcurrentcontext(); //2.描述路径 //一条路径可以绘制多条线 路径:path 路径绘制多条线:path使用了两次(2次的起点到终点),都是将线添加到某个点 uibezierpath *path = [uibezierpath bezierpath]; //设置起点 [path movetopoint:cgpointmake(50, 150)]; //添加一根线line到某个点 [path addlinetopoint:cgpointmake(250, 50)]; //画第二根线 [path movetopoint:cgpointmake(50, 250)]; [path addlinetopoint:cgpointmake(250, 100)]; //设置线宽 cgcontextsetlinewidth(ctx, 20); //设置线的连接样式 cgcontextsetlinejoin(ctx, kcglinejoinbevel); //设置线的顶角样式 cgcontextsetlinecap(ctx, kcglinecapround);// 圆角线条 //设置线条颜色 [[uicolor redcolor] set]; //3.把路径添加到上下文 cgcontextaddpath(ctx, path.cgpath); //4.把上下文当中绘制的内容渲染到跟view关联的layer cgcontextstrokepath(ctx); }

运行效果:

ios开发之quartz2d的介绍与使用详解(ios开发之quartz2d的介绍与使用详解)

6、画曲线

本塞尔曲线原理:

ios开发之quartz2d的介绍与使用详解(ios开发之quartz2d的介绍与使用详解)

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 - (void)drawrect:(cgrect)rect { //1.获取跟view相关联的上下文.】 cgcontextref ctx = uigraphicsgetcurrentcontext(); //2.描述路径 uibezierpath *path = [uibezierpath bezierpath]; //画曲线,设置起点.还有一个控制点(用来控制曲线的方向跟弯曲程度) //设置起点 [path movetopoint:cgpointmake(10, 150)]; //添加一要曲线到某个点 [path addquadcurvetopoint:cgpointmake(200, 150) controlpoint:cgpointmake(150, 10)]; //3.把路径添加到上下文当中 cgcontextaddpath(ctx, path.cgpath); //4.把上下文的内容渲染view上 cgcontextstrokepath(ctx); }

运行效果:

ios开发之quartz2d的介绍与使用详解(ios开发之quartz2d的介绍与使用详解)

7、画饼图

方法1:

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 - (void)drawrect:(cgrect)rect { nsarray *dataarray = @[@25,@25,@50]; // 画弧 cgpoint center = cgpointmake(rect.size.width * 0.5, rect.size.height * 0.5); // 半径 cgfloat radius = rect.size.width * 0.5 - 10; cgfloat starta = 0; cgfloat angle = 0; cgfloat enda = 0; for (nsnumber *num in dataarray) { starta = enda; // 遍历出第一个对象25,angle =25/100 *2π,即angle = π/2,所以为1/4圆, angle = num.intvalue / 100.0 * m_pi * 2; // 截至角度= 开始的角度+ 遍历出的对象所占整个圆形的角度 enda = starta + angle; // 顺势针画贝塞尔曲线 uibezierpath *path = [uibezierpath bezierpathwitharccenter:center radius:radius startangle:starta endangle:enda clockwise:yes]; // 设置随机颜色 [[self randomcolor] set]; // 添加一根线到圆心 [path addlinetopoint:center]; // 填充路径 [path fill]; // 描边路径 // [path stroke]; } } /** 生成随机颜色 @return uicolor */ -(uicolor *)randomcolor{ cgfloat redlevel = rand() / (float) rand_max; cgfloat greenlevel = rand() / (float) rand_max; cgfloat bluelevel = rand() / (float) rand_max; return [uicolor colorwithred: redlevel green: greenlevel blue: bluelevel alpha: 1.0]; }

运行效果:

ios开发之quartz2d的介绍与使用详解(ios开发之quartz2d的介绍与使用详解)

方法二:

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 - (void)drawrect:(cgrect)rect { cgpoint center = cgpointmake(self.bounds.size.width * 0.5, self.bounds.size.height * .5); cgfloat radius = self.bounds.size.width * 0.5 - 10; cgfloat starta = 0; cgfloat enda = 25 / 100.0 * m_pi * 2; uibezierpath *path = [uibezierpath bezierpathwitharccenter:center radius:radius startangle:starta endangle:enda clockwise:yes]; [[uicolor redcolor] set]; //添加一根线到圆心 [path addlinetopoint:center]; [path fill]; //第二个扇形 starta = enda; cgfloat angle = 25 / 100.0 * m_pi * 2; enda = starta + angle; uibezierpath *path2 = [uibezierpath bezierpathwitharccenter:center radius:radius startangle:starta endangle:enda clockwise:yes]; [[uicolor greencolor] set]; //添加一根线到圆心 [path2 addlinetopoint:center]; [path2 fill]; starta = enda; angle = 50 / 100.0 * m_pi * 2; enda = starta + angle; uibezierpath *path3 = [uibezierpath bezierpathwitharccenter:center radius:radius startangle:starta endangle:enda clockwise:yes]; [[uicolor bluecolor] set]; //添加一根线到圆心 [path3 addlinetopoint:center]; [path3 fill]; } /** 生成随机颜色 @return uicolor */ -(uicolor *)randomcolor{ cgfloat redlevel = rand() / (float) rand_max; cgfloat greenlevel = rand() / (float) rand_max; cgfloat bluelevel = rand() / (float) rand_max; return [uicolor colorwithred: redlevel green: greenlevel blue: bluelevel alpha: 1.0]; }

注:

如果想实现点击以下变换颜色可以加上如下代码:

?
1 2 3 4 5 6 - (void)touchesbegan:(nsset<uitouch *> *)touches withevent:(uievent *)event { //重绘 [self setneedsdisplay]; }

8、绘制文字

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 - (void)drawrect:(cgrect)rect { nsstring *str = @"李峰峰博客:http://www.imlifengfeng.com/"; nsmutabledictionary *dict = [nsmutabledictionary dictionary]; //设置字体 dict[nsfontattributename] = [uifont systemfontofsize:30]; //设置颜色 dict[nsforegroundcolorattributename] = [uicolor redcolor]; //设置描边 dict[nsstrokecolorattributename] = [uicolor bluecolor]; dict[nsstrokewidthattributename] = @3; //设置阴影 nsshadow *shadow = [[nsshadow alloc] init]; shadow.shadowcolor = [uicolor greencolor]; shadow.shadowoffset = cgsizemake(-2, -2); shadow.shadowblurradius = 3; dict[nsshadowattributename] = shadow; //设置文字的属性 //drawatpoint不会自动换行 //[str drawatpoint:cgpointmake(0, 0) withattributes:dict]; //drawinrect会自动换行 [str drawinrect:self.bounds withattributes:dict]; }

运行效果:

ios开发之quartz2d的介绍与使用详解(ios开发之quartz2d的介绍与使用详解)

9、加水印

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 // // viewcontroller.m // quartz2dtest // // created by 李峰峰 on 2017/2/6. // copyright © 2017年 李峰峰. all rights reserved. // #import "viewcontroller.h" #import "myview.h" @interface viewcontroller () @end @implementation viewcontroller - (void)viewdidload { [super viewdidload]; uiimageview *myimageview = [[uiimageview alloc]initwithframe:cgrectmake(0, 0, [uiscreen mainscreen].bounds.size.width, [uiscreen mainscreen].bounds.size.height)]; [self.view addsubview:myimageview]; //生成一张图片 //0.加载图片 uiimage *oriimage = [uiimage imagenamed:@"test"]; //1.创建位图上下文(size:开启多大的上下文,就会生成多大的图片) uigraphicsbeginimagecontext(oriimage.size); //2.把图片绘制到上下文当中 [oriimage drawatpoint:cgpointzero]; //3.绘制水印(虽说uilabel可以快速实现这种效果,但是我们也可以绘制出来) nsstring *str = @"李峰峰博客"; nsmutabledictionary *dict = [nsmutabledictionary dictionary]; dict[nsfontattributename] = [uifont systemfontofsize:20]; dict[nsforegroundcolorattributename] = [uicolor redcolor]; [str drawatpoint:cgpointzero withattributes:dict]; //4.从上下文当中生成一张图片 uiimage *newimage = uigraphicsgetimagefromcurrentimagecontext(); //5.关闭位图上下文 uigraphicsendimagecontext(); myimageview.image = newimage; } @end

运行效果:

ios开发之quartz2d的介绍与使用详解(ios开发之quartz2d的介绍与使用详解)

10、屏幕截图

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 -(void)touchesbegan:(nsset<uitouch *> *)touches withevent:(uievent *)event { //生成图片 //1.开启一个位图上下文 uigraphicsbeginimagecontext(self.view.bounds.size); //2.把view的内容绘制到上下文当中 cgcontextref ctx = uigraphicsgetcurrentcontext(); //uiview内容想要绘制到上下文当中, 必须使用渲染的方式 [self.view.layer renderincontext:ctx]; //3.从上下文当中生成一张图片 uiimage *newimage = uigraphicsgetimagefromcurrentimagecontext(); //4.关闭上下文 uigraphicsendimagecontext(); //把图片转成二进制流 //nsdata *data = uiimagejpegrepresentation(newimage, 1); nsdata *data = uiimagepngrepresentation(newimage); [data writetofile:@"/users/lifengfeng/downloads/imlifengfeng.jpg" atomically:yes]; }

运行效果:

截图而已,就跟普通截图一样,自己试。

总结

以上就是关于quartz2d一些常用的案例,quartz2d还可以实现更多效果,具体的根据需求去实现。希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对服务器之家的支持。

原文链接:http://www.imlifengfeng.com/blog/?p=514

如果您对该产品感兴趣,请填写办理(客服微信:xiaoxiongyidong)

为您推荐:

发表评论

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。