###Graphics Contexts图形上下文###
图形上下文(graphics context)是绘制目标,可以理解为画布,包含着绘图时的参数和设备信息。类型为CGContextRef。获取graphics context后,调用Quartz 2D的函数进行绘制、旋转等操作,还可以修改如线宽、填充颜色等绘画参数。
###获取图形上下文###
当view出现在屏幕上以及视图将要更新时,系统会调用view的drawRect:方法。我们创建UIView子类,并重写drawRect:方法,此时调用UIKit提供的方法UIGraphicsGetCurrentContext,来获取系统为我们准备好的当前绘图环境的graphics context对象,。
###路径###
路径构成一个或多个图形或子路径。子路径可以由直线、曲线或两者相结合组成。如图:
###点###
调用CGContextMoveToPoint,传入表示x和y坐标的两个float值来确定路径的起始点。
CGContextMoveToPoint(context, 50, 50);
###线段###
两个点确定一条线,线的起点为当前点,所以只需要确定终点。
1、调用CGContextAddLineToPoint,传入终点坐标来添加一条线。
CGContextAddLineToPoint(context, 100, 50);
2、调用CGContextAddLines可以添加多条线,传入点的数组,Quartz以第一个点为起点,然后用直线依次连接剩下的点。
GPoint aPoints[3];//坐标点
aPoints[0] = CGPointMake(100, 80);//坐标1
aPoints[1] = CGPointMake(130, 150);//坐标2
aPoints[2] = CGPointMake(130, 200);//坐标3
CGContextAddLines(context, aPoints, 3);
【实例1】
Quartz 2D简单绘制分割线:https://www.jianshu.com/p/52700532f854
###弧###
弧是圆的一部分,Quartz提供两个方法来创建弧。
1、CGContextAddArc,传入圆的中心点坐标(x,y)、半径、起始角度、结束角度、方向(1为时钟方向)。
CGContextAddArc(context, 100, 100, 50, M_PI_2 , 0, 1);
2、CGContextAddArcToPoint,传入两个终点的坐标值以及半径长度,Quartz以两个终点确定两条切线,从而确定弧。
【实例2】
可拖动的环形进度:http://blog.csdn.net/dolacmeng/article/details/46617517
###曲线###
二次和三次贝塞尔曲线可以确定各种各样的曲线形状,如图:
1、调用CGContextAddCurveToPoint函数,传入两个控制点、结束点,来确定一条三次贝塞尔曲线。
2、通过调用CGContextAddQuadCurveToPoint,传入控制点和结束点,确定一条二次贝塞尔曲线
【实例】贝塞尔曲线动画demo(仿美人相机效果):http://blog.csdn.net/dolacmeng/article/details/79276039
**实例中使用CAShapeLayer与UIBezierPath结合绘制,而非通过重写drawRect。UIBezierPath类可以创建基于矢量的路径,这个类在UIKit中,是Core Graphics框架关于path的一个封装。
CAShapeLayer和drawRect的比较:
- 1.drawRect:属于CoreGraphics框架,占用CPU,性能消耗大。
- 2.CAShapeLayer:属于CoreAnimation框架,通过GPU来渲染图形,节省性能。动画渲染直接提交给手机GPU,不消耗内存。
参考链接:https://www.jianshu.com/p/b1c38a3a67a9
###闭合路径###
调用CGContextClosePath,会添加一条从当前点到起始点的线来闭合当前的形状。
###椭圆###
椭圆是一个被压扁的圆形,调用CGContextAddEllipseInRect方法,传入一个用以确定椭圆边界的矩形。
CGContextAddEllipseInRect(context, CGRectMake(0, 0, 200, 100));
###矩形###
调用CGContextAddRect来添加一个矩形,传入一个包含原点坐标、宽度和高度的CGRect结构体。
CGContextAddRect(context, CGRectMake(0, 0, 200, 100));
可以一次添加多个矩形,只需调用CGContextAddRects方法,并传入CGRect结构体数组。
CGRect rects[4];
rects[0] = (CGRect){{0,0}, {50, 50}};
rects[1] = (CGRect){{100, 100}, {30, 30}};
rects[2] = (CGRect){{0,100}, {20, 20}};
rects[3] = (CGRect){{100,0}, {10, 10}};
CGContextAddRects(context, rects, 4);
CGContextSetFillColorWithColor(context, [UIColor redColor].CGColor);
CGContextFillPath(context);
###Creating a Path###
创建路径:
在图形上下文中创建路径,先调用CGContextBeginPath.然后通过调用CGContextMoveToPoint设置起点。之后就绘制添加线、弧或曲线。总之记住:
- 在开始新路径前,调用CGContextBeginPath.
- 线、弧、曲线从当前点开始绘制,所以必须调用CGContextMoveToPoint来设置当前起始点或者调用对应的便捷方法。
- 当你想要闭合当前路径,调用 CGContextClosePath方法来连接上起始点。
- 当你绘制弧时,Quartz会在当前点和弧的起始点之间绘制一条直线。
- 最后需调用绘制函数来填充(fill)或画(stroke)出路径。
如:
CGContextMoveToPoint(context, 100, 100);
CGContextAddLineToPoint(context, 200, 200);
CGContextAddLineToPoint(context, 50, 300);
CGContextClosePath(context);
CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
CGContextStrokePath(context);
当路径被绘制出来后,路径会被清除。如果想要保存路径,Quartz提供了两种数据类型来创建可重复利用的路径:CGPathRef 和 CGMutablePathRef。但操作的是CGPath而不是graphics context:
- CGPathCreateMutable 代替 CGContextBeginPath
- CGPathMoveToPoint 代替 CGContextMoveToPoint
- CGPathAddLineToPoint 代替 CGContextAddLineToPoint
- CGPathAddCurveToPoint 代替 CGContextAddCurveToPoint
- CGPathAddEllipseInRect 代替 CGContextAddEllipseInRect
- CGPathAddArc 代替 CGContextAddArc
- CGPathAddRect 代替 CGContextAddRect
- CGPathCloseSubpath 代替 CGContextClosePath
###Painting a Path###
Quartz提供方法画出(画线)或填充路径,或者同时画出(画线)并填充路径。绘制的线条特性(宽度、颜色等)、填充颜色和计算填充区域的方式都是graphics state的一部分。
可以通过下表的属性,修改画出的路径特性。一旦修改这些属性,会作用于之后的所有绘制中。
Parameter | Function to set parameter value |
---|---|
Line width | CGContextSetLineWidth |
Line join | CGContextSetLineJoin |
Line cap | CGContextSetLineCap |
Miter limit | CGContextSetMiterLimit |
Line dash pattern | CGContextSetLineDash |
Stroke color space | CGContextSetStrokeColorSpace |
Stroke color | CGContextSetStrokeColorCGContextSetStrokeColorWithColor |
Stroke pattern | CGContextSetStrokePattern |
line width是线条的宽度,路径两边各占线宽的一半。
line join表示Quartz怎么处理线条的连接处。Quartz支持的连接样式 如下:
line cap表示线的两个端点的样式:
line dash线条的样式:
Quartz提供的画线方法:
Function | Description |
---|---|
CGContextStrokePath | Strokes the current path. |
CGContextStrokeRect | Strokes the specified rectangle. |
CGContextStrokeRectWithWidth | Strokes the specified rectangle, using the specified line width. |
CGContextStrokeEllipseInRect | Strokes an ellipse that fits inside the specified rectangle. |
CGContextStrokeLineSegments | Strokes a sequence of lines. |
CGContextDrawPath | If you pass the constant kCGPathStroke, strokes the current path. See Filling a Path if you want to both fill and stroke a path. |
###Filling a Path###
当填充路径时,Quartz认为每一个路径都是闭合的,然后利用这些闭合的路径计算填充区域。一共有两种计算填充区域的方式。椭圆形、矩形等简单的路径,很好确定填充区域。但是如果有重叠的路径或者路径内包含多个路径时(如下图),有两种规则来确定填充区域。
默认的填充规则为非零绕组规则:给定一条曲线C和一个点P,构造一条从P点出发射向无穷远的射线。找出所有该射线和曲线的交点,并按如下规则统计绕组数量(winding number):每一个顺时针方向(曲线从左向右通过射线)上的交点减1,每一个逆时针方向(曲线从右向左通过射线)上的交点加1。如果绕组总数为0,表示该点在曲线外;否则,该点在曲线内。
另一个为奇偶规则。平面内的任何一点P,引出一条射线,注意不要经过多边形的顶点,如果射线与多边形的交点的个数为奇数,则点P在多边形的内部,如果交点的个数为偶数,则点P在多边形的外部。
(参考:http://blog.csdn.net/wodownload2/article/details/52151714)
【实例】中间透明的引导蒙层
http://blog.csdn.net/dolacmeng/article/details/79285679
Quartz提供的填充函数如下3-5:
Function | Description |
---|---|
CGContextEOFillPath | Fills the current path using the even-odd rule. |
CGContextFillPath | Fills the current path using the nonzero winding number rule. |
CGContextFillRect | Fills the area that fits inside the specified rectangle. |
CGContextFillRects | Fills the areas that fits inside the specified rectangles. |
CGContextFillEllipseInRect | Fills an ellipse that fits inside the specified rectangle. |
CGContextDrawPath | Fills the current path if you pass kCGPathFill (nonzero winding number rule) or kCGPathEOFill (even-odd rule). Fills and strokes the current path if you pass kCGPathFillStroke or kCGPathEOFillStroke. |
###Setting Blend Modes###
重叠模式,两个绘制重叠时,重叠部分默认的颜色为:
result = (alpha * foreground) + (1 - alpha) * background
###Clipping to a Path###
路径可以作为遮罩,进行裁剪。例如有一张大图,但只希望显示其中一部分,可以设置裁剪区域,即只显示在闭合路径内部的部分:
CGContextBeginPath (context);
CGContextAddArc (context, w/2, h/2, ((w>h) ? h : w)/2, 0, 2*PI, 0);
CGContextClosePath (context);
CGContextClip (context);
提供的裁剪方法:
Function | Description |
---|---|
CGContextClip | Uses the nonzero winding number rule to calculate the intersection of the current path with the current clipping path. |
CGContextEOClip | Uses the even-odd rule to calculate the intersection of the current path with the current clipping path. |
CGContextClipToRect | Sets the clipping area to the area that intersects both the current clipping path and the specified rectangle. |
CGContextClipToRects | Sets the clipping area to the area that intersects both the current clipping path and region within the specified rectangles. |
CGContextClipToMask | Maps a mask into the specif |
###Transforms###
在绘图前,可以通过变换矩阵(CTM)进行旋转、缩放、移动(可以理解为对坐标系操作),从而实现对即将绘制的图像进行变换。
下面的代码会把一张图片绘制到上下文:
CGContextDrawImage (myContext, rect, myImage);
//在iOS中,因为UIKit坐标系统和Quartz坐标系统不一样,需要先调用CTM变换再绘图
CGContextTranslateCTM(context, 0, rect.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
调用CGContextTranslateCTM函数,改变坐标空间原点的x和y,如在x轴上移动100个单位,在y轴上移动50个单位:
CGContextTranslateCTM (myContext, 100, 50);
调用CGContextRotateCTM来旋转坐标系。如,旋转-45度:
CGContextRotateCTM (myContext, radians(–45.));
此时部分图片内容被裁剪了,因为这部分被移到图形上下文之外了。另外需要将角度转换为弧度:
include <math.h>
static inline double radians (double degrees) {return degrees * M_PI/180;}
调用CGContextScaleCTM可以在x轴和y轴上对坐标系进行缩放,如果为负值,可以进行翻转。例如在x轴上缩放0.5倍,在y轴上缩放0.75倍:
可以结合使用多个变换,通过CGContextConcatCTM方法可将多个变换组合成一个变换。
也可以直接依次执行多个变换。如:
CGContextTranslateCTM (myContext, w/4, 0);
CGContextScaleCTM (myContext, .25, .5);
CGContextRotateCTM (myContext, radians ( 22.));
###Creating Affine Transforms###
仿射变换(affine transform)是作用于矩阵而非CTM。可以使用这些函数构造一个矩阵,然后通过函数CGContextConcatCTM应用于CTM。
仿射变换和CTM变换作用相同,都可以进行平移、旋转、缩放操作。方法和作用如下表:
Function | Use |
---|---|
CGAffineTransformMakeTranslation | To construct a new translation matrix from x and y values that specify how much to move the origin. |
CGAffineTransformTranslate | To apply a translation operation to an existing affine transform. |
CGAffineTransformMakeRotation | To construct a new rotation matrix from a value that specifies in radians how much to rotate the coordinate system. |
CGAffineTransformRotate | To apply a rotation operation to an existing affine transform. |
CGAffineTransformMakeScale | To construct a new scaling matrix from x and y values that specify how much to stretch or shrink coordinates. |
CGAffineTransformScale | To apply a scaling operation to an existing affine transform. |
.
Quartz提供了CGAffineTransformInvert方法来撤消一个矩阵。但一般可以通过保存和还原graphics state实现类似的需求。
如果想要只对某个点或某部分进行变换,可以通过CGPointApplyAffineTransform方法对点,通过CGSizeApplyAffineTransform对某部分进行操作。
【待续…】