一、Quartz2D基本概念
1、Quartz2D是一个二维图形绘制引擎,支持iOS环境和Mac OS X环境
2、Quartz2D API可以实现很多功能,如基于路径的绘图、透明度、阴影、颜色管理、反锯齿、PDF文档生成和PDF元数据访问等
3、Quartz2D API是Core Graphics框架的一部分,因此其中的很多数据类型和方法都是以CG开头的
4、Quartz2D与分辨率和设备无关,因此在使用Quartz2D绘图时,无需考虑最终绘图的目标设备
二、Core Graphics
1、该框架是一组基于C的API,可以用于一切绘图操作,这个框架的重要性,仅次于UIKit和Foundation
2、当使用UIKit创建按钮、标签或者其他UIView的子类时,UIKit会用Core Graphics将这些元素绘制在屏幕上,此外,UIEvent也会使用Core Graphics,用来帮组确定触摸时间在屏幕上所处的位置
3、因为UIKit依赖于Core Graphics,所以当引入时,Core Graphics框架会被自动引入
4、为了让开发者不必触及底层的Core Graphics的C接口,UIKit内部封装了Core Graphics的一些API,可以快速生成通用的界面元素,但是,有时候直接利用Core Graphics的C接口是很有必要和很有好处的,比如创建一个自定义的界面元素
三、Quartz2D的几个重要概念
1、图像上下文(Graphics Context) - 相当于一个画笔
1) Graphics Context是一个数据类型(CGContextRef),封装了Quartz绘制图像到输出设备的信息,输出设备可以是PDF文件、Bitmap(位图文件,一种图形文件)或者显示器的窗口上
2)Quartz中所有的对象都是绘制到一个Graphics Context中
3)当用Quartz绘图时,所有设备相关的特性都包含在Graphics Context中,换句话说,我们可以简单地给Quartz绘图序列指定不同的Graphics Context,就可将相同的图像绘制在不同的设备上,而不需要任何设备相关的计算,这些都有Quartz替我们完成
2、Quartz2D坐标系
1)Quartz中默认的坐标系统是:原定(0,0)在左下角,沿着X轴从左到右坐标值逐渐增大,沿着Y轴从下到上左键增加
2)有些技术在设置他们的graphics Context时使用了不同于Quartz的默认坐标系统,最常见的是系统原点修改为左上角
3)坐标系的转换
//相对原点旋转上下文坐标系
CGContextRotateCTM(CGContextRef c, CGFloat angle)
//相对原点平移上下文坐标系
CGContextTranslateCTM(CGContextRef c, CGFloat tx, CGFloat ty)
//缩放上下文坐标系
CGContextScaleCTM(CGContextRef c, CGFloat sx, CGFloat sy)
PS:
注意:
转换坐标系前,使用方法保存当前上下文状态
CGContextSaveGState(CGContextRef c)
坐标系转换后,使用方法恢复之前保存的上下文状态
CGContextRestoreGState(CGContextRef c)可以
3、Quartz2D的绘图顺序
1)谁后绘制谁显示在顶部,即叠加到最上面
2)利用Quartz2D绘制UIView
当在UIView子类中重写drawRect方法时,iOS会自动准备好一个图像上下文,可以通过调用UIGraphicsGetCurrentContext()来获取
3)只要一个UIView需要被刷新或者重绘,drawRect方法就会被调用,需要注意的是:重绘时应该调用setneedsDisplay,而不能直接调用drawRect,setNeedsDisplay会自动调用drawRect:
4) drawRect注意事项
drawRect:是在UIViewController的loadView和viewDidLoad两方法之后调用的
drawRect:如果试图没有设置frame,将导致该方法不能执行
如果设置UIView的contentMode属性值为UIViewContentModeRedraw,那么将在每次更改frame时自动调用drawRect:
如果使用UIView绘图,只能在drawRect:方法中获取相应的CGContextRef并绘图,而在其他方法中获取的CGContextRef不能用于绘图
4、Quartz内存管理
1)使用含有"Create"或"Copy"的函数创建的对象,使用完后必须释放,否则将导致内存泄露,使用不含有"Create"或"Copy"的函数获取的对象,则不需要释放
2)如果retain了一个对象,不再使用时需要将其release掉,可以使用Quratz2D的函数来指定retain和release一个对象,例如创建了一个CGColorSpace对象,则使用函数CGColorSpaceRetain和CGColorSpaceRelease来retain和release对象,也可以使用CoreFoundation的CFRetain和CGRelease。注意不能传递NULL值给这些函数
5、Quartz2D绘图的基础元素-路径
路径定义了一条或者多条形状或子路径
子路径可以包含一条或者多条直线或曲线
子路径也可以是一些简单的形状,例如线、圆形、矩形或者星型等
子路径还可以包含复杂的形状,例如地图轮廓或者涂鸦等
路径是可以是开放的,也可以是封闭的,对于封闭路径可以是空心的也可以是实心的
四、练习,都是在drawRect方法内操作
1、基本绘图,使用Path实现
1)获取与试图相关联的上下文对象
//提示:使用Ref声明的对象,不需要用*
CGContextRef context = UIGraphicsGetCurrentContext();
2)创建及设置路径(path)
创建路径
CGMutablePathRef path = CGPathCreateMutable();
设置路径起点
CGPathMoveToPoint(path, NULL, 50, 50);
增加路径内容
CGPathAddLineToPoint(path, NULL, 200,200);
CGPathAddLineToPoint(path, NULL, 50, 200);
封闭路径
CGPathAddLineToPoint(path, NULL, 50, 50);
CGPathCloseSubpath(path);
上面这两个函数都可以用来封闭路径,但后者在设置顶点样式时会使样式无效
3) 将路径添加到上下文
CGContextAddPath(context, path);
4)设置上下文状态
边线颜色
CGContextSetRGBStrokeColor(context, 0.7, 0.7, 0.5, 1);
填充颜色
CGContextSetRGBFillColor(context, 1, 0, 0, 1);
线宽
CGContextSetLineWidth(context, 10);
设置线条的顶点样式
CGContextSetLineCap(context, kCGLineCapButt);
设置线条的连接点样式
CGContextSetLineJoin(context, kCGLineJoinRound);
设置线条的虚线样式
context
phase 相位,虚线起始的位置,通常使用0即可,从头开始画虚线
lengths 长度的数组
count lengths数组的个数
CGFloat lengths[4] = {40.0, 40.0};
CGContextSetLineDash(context, 0.0, lengths, 2);
5)绘制路径
kCGPathStroke: 画线(空心)
kCGPathFill: 填充(实心)
kCGPathFillStroke: 即画线又填充
CGContextDrawPath(context, kCGPathFillStroke);
6)释放路径
CGPathRelease(path);
2、基本绘图,上下文的默认路径实现
1)获取上下文
CGContextRef context = UIGraphicsGetCurrentContext();
2)设置当前上下文的路径
设置路径起点
CGContextMoveToPoint(context, 50, 50);
增加路径内容
CGContextAddLineToPoint(context, 200, 200);
CGContextAddLineToPoint(context, 50, 200);
封闭路径
CGContextClosePath(context);
CGContextAddLineToPoint(context, 50, 50);
3) 设置上下文状态
CGContextSetLineWidth(context, 10); //线条宽
CGContextSetLineJoin(context, kCGLineJoinRound); //连接点样式
CGContextSetLineCap(context, kCGLineCapRound); //顶点样式
4)绘制路径,虽然没有直接定义路径,但是第二步操作,就是为上下文指定路径
CGContextDrawPath(context, kCGPathFillStroke);
3、绘制矩形
CGRect rect = CGRectMake(50, 50, 200, 200);
[[UIColor redColor] setStroke];
[[UIColor grayColor] setFill];
//绘制实心矩形
UIRectFill(rect);
//绘制空心矩形
UIRectFrame(rect);
4、绘制圆形
1. 取出上下文
CGContextRef context = UIGraphicsGetCurrentContext();
2. 设置路径
CGRect rect = CGRectMake(50, 50, 200, 200);
Ellipse圆形
CGContextAddEllipseInRect(context, rect);
3. 绘制路径
CGContextDrawPath(context, kCGPathFillStroke);
5、绘制弧度
CGContextRef context = UIGraphicsGetCurrentContext();
context 上下文
x,y 是圆弧所在圆的中心点坐标
radius 半径,所在圆的半径
startAngle endAngle 起始角度和截止角度 单位是弧度 M_PI =3.14
0度 对应是圆的最右侧点
clockwise 顺时针 0 或者逆时针 1
CGContextAddArc(context, 100, 100, 100, 0, M_PI_2, 1);
[[UIColor grayColor] setFill];
CGContextDrawPath(context, kCGPathFill);
6、绘制图像
UIImage *image = [UIImage imageNamed:@"头像1.png"];
//提示:绘制之后,就无法改变位置,也没有办法监听手势识别
在指定点绘制图像
//[image drawAtPoint:CGPointMake(50, 50)];
//会在指定的矩形中拉伸绘制
[image drawInRect:CGRectMake(0, 0, 320, 460)];
//在指定矩形区域中平铺图片
[image drawAsPatternInRect:CGRectMake(0, 0, 320, 460)];
7、绘制文字
NSString *string = @"时间到了反馈结束了的开发就流口水的减肥来看就";
//在指定点绘制文字
[string drawAtPoint:CGPointMake(0, 0) withAttributes:@{NSForegroundColorAttributeName:[UIColor redColor]}];
//在指定的矩形区域绘制文字
CGRect rect = CGRectMake(50, 50, 210, 360);
[string drawInRect:rect withAttributes:@{NSForegroundColorAttributeName:[UIColor redColor]}];
提示:如果不在drawRect方法中调用绘图方法,上下文地址都是0,不能直接进行绘制
8、绘制水印图片
1. 建立图像的上下文,需要指定新生成的图像大小
CGSize imageSize = CGSizeMake(320, 200);
UIGraphicsBeginImageContext(imageSize);
2、绘制图片
UIImage *image = [UIImage imageNamed:@"NatGeo01.png"];
[image drawInRect:CGRectMake(0, 0, imageSize.width, imageSize.height)];
3、绘制矩形
[[UIColor yellowColor]set];
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextAddRect(context, CGRectMake(50, 50, 100, 100));
CGContextDrawPath(context, kCGPathEOFill);
4、添加水印文字
NSString *str = @"我的水印";
[str drawInRect:CGRectMake(0, imageSize.height - 30, imageSize.width - 20, 30) withAttributes:@{NSForegroundColorAttributeName:[UIColor redColor]}];
5、获取到新生成的图像,从当前上下文获取到新绘制的图像
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
6、关闭图像上下文
UIGraphicsEndImageContext();