Quartz 2D属于Core Graphics(所以大多数相关方法的都是以CG开头),是iOS/Mac OSX 提供的在内核之上的强大的2D绘图引擎,并且这个绘图引擎是设备无关的。也就是说,不用关心设备的大小,设备的分辨率,只要利用Quartz 2D,这些设备相关的会自动处理。Quartz 2D能够提供的强大功能如下:
- 透明层(transparency layers)
- 阴影
- 基于path的绘图(path-based drawing)
- 离屏渲染(offscreen rendering)
- 复杂的颜色处理(advanced color management)
- 抗锯齿渲染(anti-aliased rendering)
- PDF创建,展示,解析(这部分不在这个系列之中)
- 配合Core Animation, OpenGL ES,UIKit完成复杂的功能
##1、Graphics Context
-
概述: Core Graphics API所有的操作都在⼀个上下⽂中进行。所以在绘图之前需要获取该上下文并传入执行渲染的函数中。如果你正在渲染一副在内存中的图片,此时就需要传⼊图⽚所属的上下文。获得⼀个图形上下文是我们完成绘图任务的第一步,你可以将图形上下文理解为一块画布。如果你没有得到这块画布,那么你就无法完成任何绘图操作。
-
Quartz2D提供了以下几种类型的Graphics Context:
Bitmap Graphics Context
PDF Graphics Context
Window Graphics Context
Layer Graphics Context
Printer Graphics Context
-
一个Graphics Context表⽰示一个绘制目标。它包含绘制系统用于完成绘制指令的绘制参数和设备相关信息
-
Graphics Context定义了基本的绘制属性,如颜色、裁减区域、线条宽度和样式信息、字体信息、混合模式等
-
在iOS应用程序中,如果要在屏幕上进行绘制,需要创建⼀个UIView对象,并实现它的
drawRect:
⽅法。视图的drawRect:⽅法在视图显示在屏幕上及它的内容需要更新时被调用 -
在调⽤自定义的drawRect:后,视图对象自动配置绘图环境以便能⽴即执行绘图操作
-
作为配置的⼀部分,视图对象将为当前的绘图环境创建⼀个
Graphics Context
。通过调⽤UIGraphicsGetCurrentContext()
方法可以获取当前的Graphics Context
##2、Qudrtz 2D坐标系
坐标系统定义是被绘制到Page上的对象的位置及⼤小范围,我们在用户 空间坐标系统(user-space coordination system,简称用户空间)中指定图形的位置及⼤小。坐标值是用浮点数来定义的。
Quartz中默认的坐标系统是:原点(0, 0) 在左下角。沿着X轴从左到右坐标值逐渐增大;沿着Y轴从下到上坐标值逐渐增大
有⼀些技术在设置它们的graphics context时使用了不同于Quartz的默认坐标系统。最常⻅的⼀种修改的坐标系统是原点位于左上角,⽽沿着Y轴从上到下坐标值逐渐增⼤。例如:UIView中的UIGraphicsGetCurrentContext方法返回的图形上下⽂就是用的是这种坐标系
###2.1 UIKit坐标系
-
原点(0,0)在屏幕的左上角,X轴向右正向延伸,Y轴向下正向延伸
-
iOS的像素分辨率会随设备的硬件而变化,iPhone4第⼀次引入了视⺴膜屏幕,像素分辨率为960 * 640,刚好是前⼀一代iPod和iPhone像素分辨率( 480 * 320)的两倍
-
在绘图时,需要使用“点”的概念来思考问题,⽽不是像素。也就是说在点坐标系中绘图,不是硬件的像素坐标系
-
虽然这些设备的像素分辨率不同,但用到的坐标系保持不变(以点为单位)。在iPhone4 上,⼀个点会用2像素宽度来绘制
提示:如果绘图的上下文,是使⽤UIGraphicsGetCurrentContext或者其他以UI开头的⽅法获取到的,在绘图时无需进行坐标转换
###2.2 坐标系的转换
-
CGContextRotateCTM(CGContextRef c, CGFloat angle)
⽅法可以相对原点旋转上下文坐标系 -
CGContextTranslateCTM(CGContextRef c, CGFloat tx, CGFloat ty)
⽅法可以相对原点平移上下文坐标系 -
CGContextScaleCTM(CGContextRef c, CGFloat sx, CGFloat sy)
方法可以缩放上下⽂坐标系 注意: 转换坐标系前,使⽤CGContextSaveGState(CGContextRef c)保存当前上下文 状态坐标系转换后,使用CGContextRestoreGState(CGContextRef c)可以恢复之前保存的上下⽂状态
##3、绘图方法 Quartz 2D绘图流程:
- 获取上下⽂
- 保存上下文
- 创建及设置路径
- 将路径添加到上下⽂
- 设置上下文状态(边框颜色,填充颜色,线条宽度,线段连接样式,线段⾸尾样式,虚线样式等)
- 绘制路径
- 释放路径
- 恢复上下⽂
路径:
- 路径定义了一个或多个形状,或是⼦路径。
- 一个⼦子路径可由直线,曲线,或者同时由两者构成。
- 它可以是开放的,也可以是闭合的。
- 一个子路径可以是简单的形状,如线、圆、矩形、星形;
- 也可以是复杂的形状,如山脉的轮廓或者是涂鸦。
- 路径可以是开放的,也可以是封闭的;对于封闭路径可以空心的也可以是实⼼的
在iOS应用程序中,如果要使用 Core Graphics 在屏幕上进行绘制图像需要在如下方法中进行。
drawRect:
方法 需要创建一个UIView对象,并实现它的drawRect:方法。视图的drawRect:方法在视图显示在屏幕上及它的内容需要更新时被调用,或者使用setNeedsDisplay
手动调用。
// 1. 将自定义视图添加到视图上显示
- (void)viewDidLoad {
[super viewDidLoad];
DrawView *drawView = [[DrawView alloc] initWithFrame:self.view.bounds];
[self.view addSubview:drawView];
}
// 2. 在自定义的DrawView 中绘图,当视图显示时就会调用下面的代码。
- (void)drawRect:(CGRect)rect
{
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextAddRect(ctx, CGRectMake(10, 20, 100, 100));
CGContextSetFillColorWithColor(ctx, [UIColor grayColor].CGColor);
CGContextDrawPath(ctx, kCGPathFill);
}
drawLayer: inContext:
方法 这是CALayer的代理方法,需要设置图层的代理对象并使用setNeedsDisplay
方法才能触发这个方法。
// 1.创建一个layer并设置代理为当前控制器
- (void)viewDidLoad {
[super viewDidLoad];
CALayer *dLayer = [CALayer layer];
dLayer.frame = self.view.bounds;
dLayer.delegate = self;
[self.view.layer addSublayer:dLayer];
[dLayer setNeedsDisplay]; // 调用这个方法才能触发代理方法绘图
}
// 2. 在代理方法中绘图
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx
{
// 绘制三角形
CGContextSetLineWidth(ctx, 2);
CGContextSetFillColorWithColor(ctx, [UIColor redColor].CGColor);
CGContextSetStrokeColorWithColor(ctx, [UIColor whiteColor].CGColor);
CGContextMoveToPoint(ctx, 0, 400);
CGContextAddLineToPoint(ctx, 320, 400);
CGContextAddLineToPoint(ctx, 160, 560);
CGContextAddLineToPoint(ctx, 0, 400);
CGContextFillPath(ctx); // 填充
CGContextStrokePath(ctx); // 描边
}
##4、Core Graphics上下文函数
绘图上下文相关函数
UIGraphicsGetCurrentContext():
获取当前绘图上下文,绘图前的第一步,因为所有的绘图都是在上下文完成的,可以理解为获取当前绘图的“画布”。
CGContextSaveGState():
保存上下文状态,这个函数的作用是将当前图形状态推入堆栈。之后,您对图形状态所做的修改会影响随后的描画操作,但不影响存储在堆栈中的拷贝。
CGContextRestoreGState():
恢复上下文状态,通过这个函数把堆栈顶部的状态弹出,返回到之前的图形状态。和CGContextSaveGState()配对使用。
CGContextSaveGState()
和CGContextRestoreGState()
使用举例:整个绘图都是红色,但是中间需要有个图是灰色,这种场景就可以使用这两个函数处理了。
// 先设置图形的填充颜色为红色
CGContextSetFillColorWithColor(ctx, [UIColor redColor].CGColor);
// 将之前设置的状态入栈保存
CGContextSaveGState(ctx);
CGContextAddRect(ctx, CGRectMake(10, 20, 100, 100));
// 设置中间图形的填充色修改为灰色
CGContextSetFillColorWithColor(ctx, [UIColor grayColor].CGColor);
CGContextDrawPath(ctx, kCGPathFillStroke);
// 恢复到保存前的绘图状态,那么后面的绘图就不是灰色了,而是原来的红色。
CGContextRestoreGState(ctx);
##5、使用Core Graphics绘图
- 获取当前绘图上下文,相当于创建“画布”
CGContextRef ctx = UIGraphicsGetCurrentContext();
- 设置要绘制的图形
//直线
CGContextMoveToPoint(ctx, 100, 250);
CGContextAddLineToPoint(ctx, 150, 150);
// 矩形
CGContextAddRect(ctx, CGRectMake(10, 20, 100, 100));
// 内切圆\椭圆
CGContextAddEllipseInRect(ctx, CGRectMake(120, 20, 100, 100));
// 三角形
CGContextMoveToPoint(ctx, 0, 400);
CGContextAddLineToPoint(ctx, 320, 400);
CGContextAddLineToPoint(ctx, 160, 560);
CGContextClosePath(ctx);//闭合路径
// 一个控制点的贝塞尔曲线
CGContextAddQuadCurveToPoint(ctx, 80, 100, 160, 300);
// 两个控制点的贝塞尔曲线
CGContextAddCurveToPoint(ctx, 80, 100, 240, 500, 320, 300);
// 弧线
CGContextAddArc(ctx, 240, 200, 80, 0, M_PI_2, NO);
// 绘制图片
CGRect imageFrame = CGRectMake(10, 30, 300, 300);
// 获取图片数据
NSString *imagePath = [[NSBundle mainBundle] pathForResource:@"test" ofType:@"png"];
UIImage *img = [UIImage imageWithContentsOfFile:imagePath];
// 设置上下文当前转换矩阵(CTM),否则图片倒置
CGContextTranslateCTM(context, 0, imageFrame.size.height);
CGContextScaleCTM(context, 1, -1);
CGContextDrawImage(context, imageFrame, img.CGImage); // 绘制
// 或者如下绘制,不需要设置CTM
[img drawInRect:imageFrame];
设置图形的属性(颜色、线条……)
CGContextSetLineWidth(ctx, 5); // 线宽
CGContextSetFillColorWithColor(ctx, [UIColor blueColor].CGColor); // 填充颜色
CGContextSetStrokeColorWithColor(ctx, [UIColor whiteColor].CGColor); // 线条颜色
CGContextSetShouldAntialias(ctx, YES); // 抗锯齿
CGContextSetLineCap(ctx, kCGLineCapRound); // 线头颜色
CGContextSetLineJoin(ctx, kCGLineJoinBevel); // 链接点样式
绘图(填充)
CGContextFillPath(ctx); // 填充
CGContextStrokePath(ctx); // 描边
绘制描边、填充、描边和填充、奇偶填充:
kCGPathFill,kCGPathEOFill,kCGPathStroke,kCGPathFillStroke, kCGPathEOFillStroke
CGContextDrawPath(cxt, kCGPathFillStroke);
保存上下文状态(入栈)
CGContextSaveGState(cxt);
恢复上下文状态(出栈)
CGContextRestoreGState(cxt);
##6、使用UIBezierPath绘图
UIKit中的UIBezierPath是Core Graphics框架关于path的一个封装(封装为OC的方法)。 使用UIBezierPath绘图不需要手动获取绘图上下文,当drawRect方法被调用时,UIView的绘图上下文属于当前图形上下文。
实例化一个path对象
// 不设置图形路径
UIBezierPath *path = [UIBezierPath bezierPath];
// 矩形路径
UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(50, 100, 200, 200)];
// 内切圆\椭圆路径
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(50, 100, 200, 200)];
//圆角矩形路径
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(50, 100, 200, 200) cornerRadius:20];
// 弧线路径,ArcCenter:原点、radius:半径、startAngle:起点弧度、endAngle:终端弧度、clockwise:是否顺时针
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(100, 100) radius:50 startAngle:0 endAngle:M_PI_2 clockwise:YES];
设置虚线: dashs: 虚线的虚实线的长度,count: 虚线的组成段数, phase: 设置虚线的起始位置
[path setLineDash:dashs count:3 phase:0];
添加路径
// 移动到某个点
[path moveToPoint:p1];
// 添加一条线到某个点
[path addLineToPoint:p2];
// 添加一条贝塞尔曲线
[path addQuadCurveToPoint:p1 controlPoint:p2];
// 添加一条弧线
[path addArcWithCenter:p1 radius:50 startAngle:M_PI_4 endAngle:M_PI_2 clockwise:YES];
// 闭合路径
[path closePath];
// 移除所有的点
[path removeAllPoints];
设置路径属性
// 设置填充颜色
[[UIColor redColor] setFill];
// 设置描边颜色
[[UIColor whiteColor] setStroke];
// 线宽
path.lineWidth = 5;
// 线头样式
path.lineCapStyle = kCGLineCapRound;
// 线连接点样式
path.lineJoinStyle = kCGLineJoinMiter;
// 连接点的斜距(角的内点和外点的距离),连接样式得为kCGLineJoinMiter
path.miterLimit = 20;
绘图(填充)
[path fill]; // 填充
[path stroke]; // 描边
Demo下载地址:
https://github.com/fuxinto/HfxDemo