Quartz 2D概览
页面(Page):Quartz 2D使用画笔模型——每一个绘图操作在一块输出画布上进行一层绘制(paint),这个画布就叫做页面。
绘制目标:图形内容(Graphics Context)是一个复杂的数据类型(CGContextRef),用于封装Quartz用于绘图的信息,用来在输出设备上draw images。
Quartz 2D模糊数据类型:从模糊数据类型创建对象,在应用程序中用其完成特殊的绘制并输出。
图形状态(Graphics states):Quartz 2D根据当前的图形状态来修改绘图结果。
Quartz 2D坐标系统:Quratz 使用current transformation matrix(CTM)完成了具体的坐标系统的无关性(完成了user space和device space的映射)。
内存管理:对象的所有权, Quratz 使用Core Foundation的内存管理模型,引用计数。
Graphics Contexts
Graphics Contexts代表一个绘图目的地。其包含绘图参数和所有与设备相关的信息,这些在绘制系统执行绘制命令的时候是需要的。
可以使用Quartz context创建函数或者IOS的UIKit框架去得到一个Graphics Context。
Graphics Context在代码的中的数据类型为CGContextRef,是一个不透明的data type。
当得到一个Graphics Context之后,可以使用Quratz 2D函数去绘制context,在context上进行一些操作(如translations),改变graphics 的状态参数,如line的宽度和fill color。
IOS中在一个view Graphics Context上绘制
先建立一个UIView对象,然后实现其drawRect:方法去进行绘制。
当view变的可见的时候或者其内容需要更新的时候,view的drawRect:方法会被调用。
在调用自定义的drawRect:方法之前,view对象会自动的配置其绘制环境,然后代码可以立即的绘制。
作为配置的一部分,UIView对象先为当前的绘制环境创建一个Graphics Context。
UIKit使用的缺省坐标系统与Quartz的坐标系统不相同。
创建一个bitmap Graphics Context
bitmap Graphics Context接受一个指针,指向内存缓冲区,其包含一个bitmap的存储空间。
当我们在bitmap Graphics Context上paint时,此缓冲区会被更新。
当我们释放了Graphics Context,我们将有一个在指定像素格式下的完全更新bitmap。
paths
一个path有一个或者多个shapes,或者subpaths定义。
path的创建和path的painting
首先创建一个path
当我们在graphics context下创建一个path,可应该先调用Quartz的函数CGContextBeginPath,
接着设置第一个shape或者subpath的开始点,在path中调用函数CGContextMoveToPoint。
当初始点设置好以后,就可以添加lines,arcs,curves,但要注意一下几点:
在开始新的path之前,要调用函数CGContextBeginPath。
lines,arcs,curves都是从current point开始绘制,一个空的path没有current point,我们必须调用CGContextMoveToPoint去设置或者调用一个函数隐含的设置了当前值。
调用函数CGContextClosePath去关闭current subpath。接下来会开始一个新的subpath,即时我们不去那个设置开始点。
当我们绘制一个arcs时,Quratz会在current point 和start point之间绘制一条直线。
创建一个path不等于绘制path,所以我们必须调用paint 函数去fill 或者stroke path。
当我们paint一个path之后,graphics context会被刷新,但是我们有些时候不想就失去此path,特别是在一些复杂的场景下我们想多次用到。 Quartz提供了两个数据类型用于创建可重复用的path(CGPathRef和CGMutablePathRef)。
我们可以调用函数CGPathCreateMutable去创建一个可变的CGPath对象,然后去添加lines,arcs,curves等。
当想要去显示时,要请求Quartz去paint
我们可以选择path的stroke(勾画),path的fill(填充)。我们也可以使用path去限制其他对象drawing的范围,此称为clipping area。
the building blocks(建筑块???)
subpaths(路径过程)是由lines,arcs(弧形),curves(曲线)组建成的,Quartz也提供了一些方便的函数用于添加矩形和椭圆。
points(点):我们可以调用函数CGContextMoveToPoint去指定一个新的subpath的开始位置。Quratz会保存当前点(current point)的记录。
Lines:一个line定义了他的endpoint,line的开始点为current(当前) point。我们用函数CGContextAddLineToPoint去在subpath上添加一条线。
Arcs:Arcs是圆的一部分。Quratz提供了两个函数用来创建arcs。
函数CGContextAddArc用来从一个圆中创建一个曲线段。
函数CGContextAddArcToPoint是一个理想的方法用来圆一个矩形的角。Quratz使用我们提供的endpoint去创建两个切线。
curves:Quadratic 和Bezier 曲线可以指定很多形状的曲线。
使用函数CGContextAddQuadCurveToPoint去使用添加一个二次Bezier曲线,要我们指定一个control point和endpoint。
使用函数CGContextAddCurveToPoint去使用一个三次Bezier 曲线,要我们指定control point和endpoint。
ellipses:可以在current path 下通过调用函数CGContextAddEllipseInRect:添加一个椭圆。
Rectangles:通过调用函数CGContextAddRect去在current path下添加一个矩形。
closing a subpath
应用程序应该调用CGContextClosePath去关闭current subpath。
当closing 一个subpath之后,如果应用程序又添加一个额外的line,arcs或者其他的,Quartz会开始一个新的subpath。
Paint path
影响stroking(画笔)的参数,这些参数是graphics state的一部分
CGContextSetLineWidth 设置线的宽度(粗细)
CGContextSetLineJoin
CGContextSetLineCap
CGContextSetMiterLimit
CGContextSetLineDash 画虚线
CGContextSetStrokeColor 设置线条颜色
CGContextSetStrokeColorSpace
CGContextSetStrokeColorWithColor
CGContextSetStrokePattern
CGContextSetRGBFillColor 填充颜色
给路径中增加图形
CGContextAddRect 在路径中增加以矩形
CGContextAddRects 在路径中增加多个矩形
CGContextAddArcToPoint 在矩形中增加一个弧线
CGContextAddArc 在路径中增加一个圆弧
stroking path的函数
CGContextStrokePath 用非填充的方式绘制当前路径
CGContextFillPath 以填充的方式绘制当前路径
CGContextStrokeRect 直接在路径中以非填充方式绘制矩形
CGContextFillRect 以填充的方式在直接在当前路径中绘制矩形
CGContextStrokeRectWithWidth 以指定的宽度绘制矩形
CGContextStrokeEllipseInRect 绘制椭圆
CGContextStrokeLineSegments 绘制一连串线条
CGContextDrawPath 后来添加入此序列的,调用此方法前要关闭路径 绘制当前路径,可以选填充绘制或不填充绘制
Transforms
Quartz 2D定义了两个完全独立的坐标空间:用户空间,其代表当前的页面,设备空间,其代表设备的原始分辨率。
Quartz中和转换相关的函数
修改当前转换矩阵:
在我们改变CTM之前,我们需要去保存graphics 状态,以便我们在绘制之后还原其状态。
在我们绘制一个image之前,我们可以操纵CTM去rotate,scale,translate。
Translation是移动坐标系统的初始点,通过调用函数CGContextTranslateCTM去改变
Rotation是改变坐标系统指定的角度。通过调用函数CGContextRotateCTM去指定具体的角度,弧度。
Scaling改变坐标空间的scale。能有效的压缩或者延伸image。我们通过调用函数CGContextScaleCTM去指定具体的x轴和y轴的scaling factors。
Concatenation累积转换,一次发起两次或者两次以上的transformations。
累积转换的顺序不同我们会得到不同的结果:
创建affine(仿射线) transforms
affine transform函数CGContextConcatCTM可以在矩阵上应用
affine transform函数有操作的,也有返回的,返回的类型为CGAffineTransform .
绘制Images
要注意的一点是 :因为用Quartz image 绘制images是倒置的,Quartz image在初始点为左下角的位置绘制,但是UIView将其初始点放置在左上角。
首先要获取一个图片
NSString *imagePath = [[NSBundle mainBundle] pathForResource:@"Demo.png" ofType:nil]; 获取文件在项目中的路径
UIImage *img = [UIImage imageWithContentsOfFile:imagePath]; 根据文件名加载图片到img。扩展:让img不会倒置见备注。
其次:CGImageRef image = [img CGImage]; 获取一个atlas图像的CGImageRefimg上下文句柄。扩展:在CGImageRef中不会倒置的实现见备注 CGImageRef是一个指向Quartz 2D中的一个内部结构的指针,这个结构包含了这幅图像所有和显示相关的信息。
将图形绘制到上下文中
CGContextDrawTiledImage 平铺图形显示
CGContextClip 将路径中的矩形设置为裁剪区
CGContextClipToRect 是上面和在路径中添加矩形区的结合方法
CGContextGetClipBoundingBox 获取上下问中的剪切矩形
最后在dealloc中需要释放 CGImageRelease(image);
触摸
了解触摸事件
在Cocoa中,代表触摸对象的类是UITouch。UITouch对象直接包括触摸的详细信息。
当用户触摸屏幕后,就会产生相应的事件,所有相关的UITouch对象都被包装在事件中,被程序交由特定的对象来处理。
UITouch类中包含5个属性:
window:触摸产生时所处的窗口。由于窗口可能发生变化,当前所在的窗口不一定是最开始的窗口。
view:触摸产生时所处的视图。由于视图可能发生变化,当前视图也不一定时最初的视图。
tapCount:轻击(Tap)操作和鼠标的单击操作类似,tapCount表示短时间内轻击屏幕的次数。因此可以根据tapCount判断单击、双击或更多的轻击。
timestamp:时间戳记录了触摸事件产生或变化时的时间。单位是秒。
phase:触摸事件在屏幕上有一个周期,即触摸开始、触摸点移动、触摸结束,还有中途取消。而通过phase 可以查看当前触摸事件在一个周期中所处的状态。phase是UITouchPhase类型的,这是一个枚举配型,包含了
UITouchPhaseBegan(触摸开始)
UITouchPhaseMoved(接触点移动)
UITouchPhaseStationary(接触点无移动)
UITouchPhaseEnded(触摸结束)
UITouchPhaseCancelled(触摸取消)
UITouch类中包含如下成员函数
- (CGPoint)locationInView:(UIView *)view: 函数返回一个CGPoint类型的值,表示触摸在view这个视图上的位置,这里返回的位置是针对view的坐标系的。 调用时传入的view参数为空的话,返回的时触摸点在整个窗口的位置。
- (CGPoint)previousLocationInView:(UIView *)view: 该方法记录了前一个坐标值,函数返回也是一个CGPoint类型的值, 表示触摸在view这个视图上的位置, 这里返回的位置是针对view的坐标系的。调用时传入的view参数为空的话,返回的时触摸点在整个窗口的位置。
当手指接触到屏幕,不管是单点触摸还是多点触摸,事件都会开始,直到用户所有的手指都离开屏幕。 期间所有的UITouch对象都被包含在UIEvent事件对象中,由程序分发给处理者。事件记录了这个周期中所有触摸对象状态的变化。
只要屏幕被触摸,系统就会报若干个触摸的信息封装到UIEvent对象中发送给程序, 由管理程序UIApplication对象将事件分发。一般来说,事件将被发给主窗口,然后传给第一响应者对象(FirstResponder)处理。
关于响应者的概念,通过以下几点说明
响应者对象(Response object):响应者对象就是可以响应事件并对事件作出处理。
第一响应者(First responder):当前接受触摸的响应者对象被称为第一响应者,即表示当前该对象正在与用户交互,它是响应者链的开端。
响应者链(Responder chain):响应者链表示一系列的响应者对象。
管理事件分发:视图对触摸事件是否需要作处li回应可以通过设置视图的userInteractionEnabled属性。
如果要让视图接收多点触摸,需要设置它的multipleTouchEnabled属性为YES,默认状态下这个属性值为NO,即视图默认不接收多点触摸。
处理 触摸事件
首先触摸的对象是视图,而视图的类UIView继承了UIRespnder类, 但是要对事件作出处理,还需要重写UIResponder类中定义的事件 处理函数。根据不通的触摸状态,程序会调用相应的处理函数,见右
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event; 当手指接触屏幕时,就会调用touchesBegan:withEvent方法;
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event; 当手指在屏幕上移时,动就会调用touchesMoved:withEvent方法;
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event; 当手指离开屏幕时,就会调用touchesEnded:withEvent方法;
-(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event; 当触摸被取消(比如触摸过程中被来电打断),就会调用touchesCancelled:withEvent方法。
上面的四个事件方法,在开发过程中并不要求全部实现,可以根据需要重写特定的方法。
对于这4个方法,都有两个相同的参数:NSSet类型的touches和UIEvent类型的event。
touches表示触摸产生的所有UITouch对象
event表示特定的事件
在这几个事件中,都可以拿到触摸对象,然后根据其位置,状态,时间属性做逻辑处理。