Quartz2D详解

什么是Quartz2D?

简单来说,Quartz2D是用C语言封装的二维绘图引擎,同时支持iOS和Mac系统,利用Quartz2D,我们可以完成以下工作:

  • 绘制图形 : 线条\三角形\矩形\圆\弧等
  • 绘制文字
  • 绘制\生成图片(图像)
  • 读取\生成PDF
  • 截图\裁剪图片
  • 自定义UI控件

学习Quartz2D的价值在哪里

虽然Quartz2D属于偏底层的API,并且UIKit已经把我们经常使用的控件封装好,但是,面对一些自定义控件,复杂的控件,UIKit就显得有些无力了。

学好Quartz2D,我们就可以:

  • 绘制一些系统UIKit框架中不好展示的内容,例如饼图,环形进度条,统计图表
  • 自定义一些控件
  • 不添加UI控件的情况下,使UI内容更丰富
  • ……

在iOS中,我们所看见的大部分控件都是利用Quartz2D绘制出来的。

什么是图形上下文

简单来说,图形上下文就相当于一块画布,我们可以在这块画布上画任何我们想要的内容,最后展示在View上。

然而,这个画布一定要在drawRect:方法里面去获取,实现才有效,下面就具体讲下这个方法。

drawRect:

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

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

在drawRect:方法中取得上下文后,就可以绘制东西到view上

drawRect如何绘图到View上

View内部有个layer(图层)属性,drawRect:方法中取得的是一个Layer Graphics Context,因此,绘制的东西其实是绘制到view的layer上去了

View之所以能显示东西,完全是因为它内部的layer

drawRect:方法如何调用的

  • 当view第一次显示到屏幕上时,系统会创建好一个跟当前view相关的Layer上下文
  • 系统会通过此上下文,在drawRect:方法中绘制好当前view的内容
  • 主动让view重绘内容的时候,调用setNeedsDisplay或者setNeedsDisplayInRect:。我们主动调用drawRect:方法是无效的。
  • 调用view的setNeedsDisplay或者setNeedsDisplayInRect:时
  • 注意:setNeedsDisplay和setNeedsDisplayInRect:方法调用后,屏幕并不是立即刷新,而是会在下一次刷新屏幕的时候把绘制的内容显示出来。

也正是系统会在调用这个方法之前创建一个与该view相关的上下文,才让我们可以在drawRect:方法中绘制。

注意:在其他地方拿不到view相关的上下文,所以不能实现绘制。

自定义View的步骤

如何利用Quartz2D绘制东西到view上?

  1. 新建一个类,继承自UIView
  2. 实现- (void)drawRect:(CGRect)rect方法,然后在这个方法中
  3. 取得跟当前view相关联的图形上下文
  4. 绘制相应的图形内容
  5. 利用图形上下文将绘制的所有内容渲染显示到view上面

下面我就介绍如何使用Quartz2D画各种图形。

实现代码

写文字

NSString *text = @"我要写的文字";
UIFont *font = [UIFont boldSystemFontOfSize:20.f];
UIColor *color = [UIColor purpleColor];
NSDictionary *attributes = @{NSForegroundColorAttributeName:color,
                             NSFontAttributeName:font};
[text drawInRect:CGRectMake(10, 20, 80, 20) withAttributes:attributes];

画圆

CGContextRef context = UIGraphicsGetCurrentContext();  
CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);//设置画笔颜色
CGContextSetFillColorWithColor(context, [UIColor yellowColor].CGColor);//设置填充颜色
CGContextSetLineWidth(context, 5.f);//线的宽度
CGContextAddArc(context, 100, 200, 15, 0, M_PI * 2, 0);//添加一个圆
CGContextDrawPath(context, kCGPathStroke);//绘制路径

void CGContextAddArc(CGContextRef c,CGFloat x, CGFloat y,CGFloat radius,CGFloat startAngle,CGFloat endAngle, int clockwise)

  • x,y为圆点坐标
  • radius半径
  • startAngle为开始的弧度
  • endAngle为 结束的弧度
  • clockwise 0为顺时针,1为逆时针
  • ps:1弧度=180°/π (≈57.3°) 度=弧度×180°/π 360°=360×π/180 =2π 弧度

CGContextDrawPath(context, kCGPathStroke)这个方法的第二个参数是一个枚举

  • kCGPathFill 填充非零绕数规则,
  • kCGPathEOFill 表示用奇偶规则,
  • kCGPathStroke 路径,
  • kCGPathFillStroke 路径填充,
  • kCGPathEOFillStroke 表示描线,不是填充

如果你只想画一个圆而不需要填充就用kCGPathStroke,如果你不想要路径,只想填充就用kCGPathFil,如果你既要路径又要填充就要使用kCGPathFillStroke

如果只是绘制路径还可以使用这个方法

void CGContextStrokePath(CGContextRef c)

如果只是填充还可以使用这个方法

void CGContextFillPath(CGContextRef c)

画直线

CGPoint aPoints[2];//坐标点
aPoints[0] =CGPointMake(100, 80);//坐标1
aPoints[1] =CGPointMake(130, 80);//坐标2
CGContextAddLines(context, aPoints, 2);//添加线
CGContextDrawPath(context, kCGPathStroke); //根据坐标绘制路径

CGContextAddLines(CGContextRef c, const CGPoint points[],size_t count)

points[]坐标数组,count点个数

如果你不需要画折线,还有一种简单的写法

CGContextMoveToPoint(context, 100, 80); //移动到坐标1
CGContextAddLineToPoint(context, 130, 80);//从坐标一画线到坐标2

画矩形

CGRect rect1 = CGRectMake(100, 120, 20, 20);//创建一个rect
CGContextAddRect(context, rect1);//添加一个矩形
CGContextStrokePath(context);//绘制路径
//如果要填充要使用另外一个方法CGContextFillPath(context);

对于矩形,我们可以使用一下两个简便方法绘制

绘制空心矩形(不填充)

void CGContextStrokeRect(CGContextRef __nullable c, CGRect rect)

绘制实心矩形(填充)

void CGContextFillRect(CGContextRef __nullable c, CGRect rect)

画扇形

画扇形,也就画圆,只不过是设置角度的大小,形成一个扇形。

下面代码以10为半径围绕圆心画指定角度扇形。

CGContextMoveToPoint(context, 160, 180);//移到起始点,即扇形的圆心
CGContextAddArc(context, 160, 180, 30,  -60 * M_PI / 180, -120 * M_PI / 180, 1);//添加扇形
CGContextClosePath(context);//闭合路径(画扇形一定要有起始点和终点,且要闭合)
CGContextDrawPath(context, kCGPathFillStroke); //绘制路径

画椭圆

CGContextAddEllipseInRect(context, CGRectMake(160, 180, 20, 8)); //椭圆  
CGContextDrawPath(context, kCGPathFillStroke); 

画三角形

画三角形只要三个点就行跟画一条线方式一样,把三点连接起来

CGPoint sPoints[3];//坐标点  
sPoints[0] =CGPointMake(100, 220);//坐标1  
sPoints[1] =CGPointMake(130, 220);//坐标2  
sPoints[2] =CGPointMake(130, 160);//坐标3  
CGContextAddLines(context, sPoints, 3);//添加线  
CGContextClosePath(context);//封起来  
CGContextDrawPath(context, kCGPathFillStroke); //根据坐标绘制路径

画贝塞尔曲线

二次曲线

CGContextMoveToPoint(context, 120, 300);//设置Path的起点  
CGContextAddQuadCurveToPoint(context,190, 310, 120, 390);//设置贝塞尔曲线的控制点坐标和终点坐标  
CGContextStrokePath(context);  

三次曲线函数

CGContextMoveToPoint(context, 200, 300);//设置Path的起点  
CGContextAddCurveToPoint(context,250, 280, 250, 400, 280, 300);//设置贝塞尔曲线的控制点坐标(2个)和终点坐标
CGContextStrokePath(context); 

画图片

UIImage *image = [UIImage imageNamed:@"apple.jpg"];
[image drawInRect:CGRectMake(60, 340, 20, 20)];//在坐标中画出图片

图形上下文栈的操作

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

void CGContextSaveGState(CGContextRef c)

将栈顶的上下文出栈,替换掉当前的上下文

void CGContextRestoreGState(CGContextRef c)

矩阵操作

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

缩放

void CGContextScaleCTM(CGContextRef c, CGFloat sx, CGFloat sy)

旋转

void CGContextRotateCTM(CGContextRef c, CGFloat angle)

平移

void CGContextTranslateCTM(CGContextRef c, CGFloat tx, CGFloat ty)


想了解更多内容,可以点击以下链接:

http://blog.csdn.net/rhljiayou/article/details/9919713

http://www.jianshu.com/p/0e785269dccc


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念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

推荐阅读更多精彩内容