前言:
虽然UIKit框架给我们提供了很多好用的UI控件,如:UIButton,UIImage,UILabel,这些基本能满足我们大部分开发需求,但是对于一些比较复杂的,比较个性化的UI,如需要显示:饼图,柱状图等,这个时候就需要我们自定义View了,这时可以利用Quartz2D技术将控件内部的结构画出来,自定义控件的样子。然后显示在我们的界面上。
这部分内容在开发中使用的也是挺普遍的,由于时常会忘记一些内容,特写一篇文章记录一下,方便日后使用的时候查看,同时再加深一下自己的印象,也希望能帮助到一些朋友,谢谢。
一:Quartz 2D介绍
Quart2D是CoreGraphic的一部分,基于C的API,一个二维绘图引擎,同时支持iOS和Mac系统。这个绘图引擎是设备无关的,也就是说,不用关心设备的大小,设备的分辨率,只要利用Quartz 2D,这些设备相关的会自动处理。在使用的时候需要导入CoreGraphics.framework
功能:
绘制图形 : 线条\三角形\矩形\圆\弧等
绘制文字
绘制\生成图片(图像)
读取\生成PDF
截图\裁剪图片
自定义UI控件
二:图形上下文
在ios中绘制图形,离不开图形上下文,它用来包含绘制的结果,然后把这个结果渲染到屏幕上去,而Quartz 2D的容器就是CGContextRef数据模型。这种数据模型是C的结构体,存储了渲染到屏幕上需要的一切信息。图形上下文就相当于画布,不同类型的画布就是决定着画得内容将展示在哪里。
类型:
Bitmap Graphics Context
PDF Graphics Context
Window Graphics Context
Layer Graphics Context
Printer Graphics Context
作用:
1:保存绘图信息、绘图状态
2:决定绘制的输出目标(绘制到什么地方去?)(输出目标可以是PDF文件、Bitmap或者显示器的窗口上)
三:利用Quartz 2D自定义view
drawRect:方法介绍
自定义View需要新建一个View继承自UIView,默认的就会有一个drawRect方法,调用自定义的View中的drawRect:方法之前,视图对象会自动配置其绘制环境,使代码可以立即进行绘制。作为这些配置的一部分,UIView对象会为当前绘制环境创建一个图形上下文(对应于CGContextRef封装类型)。在其他地方拿不到view相关的上下文,所以不能实现绘制。
想要把绘制的图像显示到view上,图形上下文必须跟view相关联,才能将内容绘制到view上面,只有在drawRect:方法中才能取得跟view相关联的图形上下文,View内部有个layer(图层)属性,drawRect:方法中取得的是一个Layer Graphics Context,因此,绘制的东西其实是绘制到view的layer上去了,View之所以能显示东西,完全是因为它内部的layer。所以这个方法是自定义view必须要实现的方法。
drawRect:方法的调用
*当视图第一次加载的时候就会调用
*当视图重新绘制的时候会调用
注意:
drawRect方法不能直接手动调用,手动调用获取不到正确的上下文,只能系统自动调用
通过调用视图的setNeedsDisplay来进行重绘,setNeedsDisplay:这个方法中内部会自动调用drawRect方法.
自定义view的步骤:
新建一个类,继承自UIView
实现- (void)drawRect:(CGRect)rect方法,然后在这个方法中
取得跟当前view相关联的图形上下文
绘制相应的图形内容
利用图形上下文将绘制的所有内容渲染显示到view上面
四:利用Quartz 2D自定义view的实例
此实例都是通过c语言实现,也可以通过oc语言路径实现
1>:绘制一条线段
获取图形上下文 CGContextRef ref=UIGraphicsGetCurrentContext();
新建一个图形的起点 CGContextMoveToPoint(ref, 20, 20);
添加一个线段到某个点 CGContextAddLineToPoint(ref, 100, 100);
渲染 如果只是绘制一条线段,需要用stroke来渲染 CGContextStrokePath(ref);
2>:绘制三角形
获取图型上下文 CGContextRef ref=UIGraphicsGetCurrentContext();
// 绘制图形如果设置线段的时候,没有移动到新的线段的起点,会默认以上一个线段的结束点作为起点
移动到某一点新建一个图形的起点 CGContextMoveToPoint(ref, 50, 20);
添加一个线段到某个点 CGContextAddLineToPoint(ref, 100, 100);
绘制第二条线段 CGContextAddLineToPoint(ref, 20, 40);
绘制第三条线段 CGContextAddLineToPoint(ref, 50, 20);
渲染 CGContextStrokePath(ref);
3>:绘制矩形
获取上下文 CGContextRefref=UIGraphicsGetCurrentContext();
绘制图形 CGContextAddRect(ref,CGRectMake(30, 20, 100, 50));
渲染 CGContextFillPath(ref);
4>:绘制椭圆
获取上下文 CGContextRefref=UIGraphicsGetCurrentContext();
绘制图形 CGContextAddEllipseInRect(ref,CGRectMake(30, 20, 100, 50));
渲染 CGContextFillPath(ref);
5>:绘制圆
获取上下文 CGContextRefref=UIGraphicsGetCurrentContext();
绘制图形 CGContextAddArc(ref, 100, 100, 50, 0,M_PI*2, 0);
渲染 CGContextFillPath(ref);
注:CGContextAddArc参数含义 <#CGFloat x#>, <#CGFloat y#>,用来表示圆心,<#CGFloat radius#>:表示圆的半径,<#CGFloat startAngle#>:表示开始角度,<#CGFloat endAngle#>:结束角度,<#int clockwise#>:用来表示顺时针还是逆时针,0.表示顺时针,1.表示逆时针。oc相反。
6>:绘制实心的圆弧
获取上下文 CGContextRefref=UIGraphicsGetCurrentContext();
绘制圆弧 绘制线段先
CGContextMoveToPoint(ref, 100, 100);
CGContextAddLineToPoint(ref, 150, 100);
绘制弧线 CGContextAddArc(ref, 100, 100, 50, 0, M_PI_2, 0);
闭合路径 CGContextClosePath(ref);
渲染 CGContextFillPath(ref);
设置图形或线段样式的方法:通用
// 设置线条颜色 红色 CGContextSetRGBStrokeColor(ref, 1.0, 0, 0, 1.0);
// 设置线条宽度 CGContextSetLineWidth(ref, 10);
// 设置线条的起点和终点的样式 kCGLineCapButt, 默认 kCGLineCapRound, 圆角 kCGLineCapSquare 没什么效果
CGContextSetLineCap(ref, kCGLineCapRound);
// 设置线条的连接点的样式 kCGLineJoinMiter,默认样式 kCGLineJoinRound,圆角样式 kCGLineJoinBevel:切角样式
CGContextSetLineJoin(ref, kCGLineJoinRound);
图形上下文栈的两种方法:图形显示的原理
//先把当前的图形上下文状态保存到栈 在设置属性之前保存 保存的是系统默认状态,如果在设置之后保存,保存的是设置之后的 CGContextSaveGState(ref);
//设置绘图状态 如果想恢复原始的设置用此方法,还原绘图状态,最原始的状态,会从图形上下文状态栈当中把当前的覆盖掉 CGContextRestoreGState(ref);
图形上下文的矩阵变化
利用矩阵操作,能让绘制到上下文中的所有路径一起发生变化, 给上下文做矩阵变化,一定要在添加路径之前
平移 CGContextTranslateCTM(CGContextRef c, CGFloat tx, CGFloat ty)
缩放 CGContextScaleCTM(CGContextRef c, CGFloat sx, CGFloat sy)
旋转 CGContextRotateCTM(CGContextRef c, CGFloat angle)
渲染的方式
渲染的方式Stroke空心 CGContextStrokePath(ref);
渲染的方式Fill实心 CGContextFillPath(ref);
通过mode kCGPathFill, kCGPathEOFill, kCGPathStroke,kCGPathFillStroke, kCGPathEOFillStroke 。 CGContextDrawPath(ref, kCGPathEOFillStroke);
用来渲染指定范围内的视图 CGContextStrokeRect(ref, CGRectMake(0, 0, 200, 200));
实际应用:饼图
积偶填充规则
结论:被覆盖过奇数次的点填充,被覆盖过偶数次的点不填充
非零环绕数规则:
结论:
默认填充模式:kCGPathFill,从左到右跨过, +1。从右到左跨过, -1。最后如果为0,那么不填充,否则填充。
也就是说:
观察最里面的圆依次与外面的圆相比较,看旋转方向,如果顺时针和逆时针画出来的圆个数相等(算最里面的圆)则最里面的圆等于0,不填充,反之不等于0,填充。依次类推,再拿第二个圆与外面的圆比较,同理。
五:利用UIKit进行绘图
1>:绘制文字
NSString*text =@"sdafsafg";
//创建一个可变字典,来储存文字的状态 NSMutableDictionary*attributesDict = [NSMutableDictionarydictionary];
attributesDict[NSFontAttributeName] = [UIFontsystemFontOfSize:20];//大小
attributesDict[NSForegroundColorAttributeName] = [UIColorredColor];//颜色
drawAtPoint这个方法文字不可以自动换行表示绘制的位置[textdrawAtPoint:CGPointMake(0, 0)withAttributes:attributesDict];
drawInRect这个方法文字可以自动换行 表示绘制的范围 只能在某一个范围
[text drawInRect:rect withAttributes:attributesDict];
2>:绘制图片
UIImage *image = [UIImage imageNamed:@"图片.png"];
// 设置矩形剪裁区域,一定要在渲染之前 把超出剪裁区域的部分全部剪裁掉UIRectClip(CGRectMake(0, 0, 50, 50));
// 平铺 会把给定的范围填充满图片 [image drawAsPatternInRect:rect];
// 默认显示的尺寸就是图片的尺寸 [image drawAtPoint:CGPointZero];
//显示的图片尺寸就是设置的范围 //[image drawInRect:CGRectMake(0, 0, 100, 100)];