什么是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上?
- 新建一个类,继承自UIView
- 实现- (void)drawRect:(CGRect)rect方法,然后在这个方法中
- 取得跟当前view相关联的图形上下文
- 绘制相应的图形内容
- 利用图形上下文将绘制的所有内容渲染显示到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)
想了解更多内容,可以点击以下链接: