在绘制复杂图形的时候会用到路径,在2.2节的实例中如下代码都是与路径有关系的。
CGContextMoveToPoint(context, 75, 10); CGContextAddLineToPoint(context, 10, 150); CGContextAddLineToPoint(context, 160, 150); CGContextClosePath(context); CGContextDrawPath(context, kCGPathFillStroke);
我们用路径来描述矩形、圆及其他想要画的2D几何图形。通过路径可以对这些几何图形进行描边、填充和描边填充处理。Core Graphics(Quartz 2D)中有4个基本图元用于描述路径:点、线段、弧和贝塞尔(Bézier)曲线。
点是二维空间中的一个位置,不要把它想成像素,一个点完全不占空间,所以画一个点不会在屏幕上显示任何东西,我们可以在路径里加入很多的点,想加多少加多少。
线段由两个点定义:起点和终点。线段可以通过描边绘制出来,我们可以通过设置图形上下文,如画笔宽度或者颜色等参数,就可以绘制出两点之间的线段。线段没有面积,所以它们不能被填充。我们可以用一组线段或曲线组成一个具有闭合路径的几何图形,然后填充它。
弧可以由一个圆心点、半径、起始角和结束角描述的。圆是弧的特例,只需要设置为起始角0°,结束角为360°就可以了。因为弧是占有一定面积的路径,所以可以被填充、描边和描边填充出来。
贝塞尔(Bézier)曲线是法国数学家贝塞尔在工作中发现,任何一条曲线都可以通过与它相切的控制线两端的点的位置来定义。因此,贝塞尔曲线可以用4个点描述,其中两个点描述两个端点,另外两个描述每一端的切线。贝塞尔曲线可以分为:二次方贝塞尔曲线(图2-9)和高阶贝塞尔曲线(图2-10是三次方贝塞尔曲线)。
![]() |
![]() |
图2-9 二次方贝塞尔曲线 | 图2-10 三次方贝塞尔曲线 |
下面解释一下2.2节实例中有关路径的代码,其中下面4条语句是绘制路径。
CGContextMoveToPoint(context, 75, 10); CGContextAddLineToPoint(context, 10, 150); CGContextAddLineToPoint(context, 160, 150); CGContextClosePath(context);
其中,CGContextMoveToPoint (context,75,10)语句是在(75,10)绘制起始点。CGContextAddLineToPoint (context,10,150)是绘制从(75,10)到(10,150)线段。CGContextAddLineToPoint (context,160,150)是绘制从(10,150)到(160,150)线段。这样通过上面3条语句就定义了一个三角形路径,如图2-11所示,路径是按照(75,10)→(10,150)→(160,150)顺序定义的,因此它不是闭合的。如果需要闭合可以调用CGContextClosePath(context)函数实现,如图2-12所示。
![]() |
![]() |
图2-11 非闭合路径 | 图2-12 闭合路径 |
定义和绘制路径是两个不同的操作,先定义路径,再绘制它。CGContextDrawPath(context,kCGPathFillStroke)函数实现绘制路径,其中kCGPathFillStroke参数是填充描边处理,此外还有:kCGPathFill和kCGPathStroke,分别代表填充和描边处理。
下面介绍贝塞尔曲线定义路径的实例。如图2-13所示是半个瓶子的轮廓,它的轮廓很复杂,我们分成很多贝塞尔曲线,然后找到贝塞尔曲线的控制点,就可以绘制它了。
图2-13 曲线定义路径实例
请参照2.2节构建工程BezierCurve。自定义视图MyView.m代码如下。
#import "MyView.h" @implementation MyView - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { // Initialization code } return self; } - (void)drawRect:(CGRect)rect { CGContextRef cgContext = UIGraphicsGetCurrentContext(); CGContextMoveToPoint(cgContext, 333, 0); ① CGContextAddCurveToPoint(cgContext, 333, 0, 332, 26, 330, 26); ② CGContextAddCurveToPoint(cgContext, 330, 26, 299, 20, 299, 17); ③ CGContextAddLineToPoint(cgContext, 296, 17); CGContextAddCurveToPoint(cgContext, 296, 17, 296, 19, 291, 19); CGContextAddLineToPoint(cgContext, 250, 19); CGContextAddCurveToPoint(cgContext, 250, 19, 241, 24, 238, 19); CGContextAddCurveToPoint(cgContext, 236, 20, 234, 24, 227, 24); CGContextAddCurveToPoint(cgContext, 220, 24, 217, 19, 216, 19); CGContextAddCurveToPoint(cgContext, 214, 20, 211, 22, 207, 20); CGContextAddCurveToPoint(cgContext, 207, 20, 187, 20, 182, 21); CGContextAddLineToPoint(cgContext, 100, 45); CGContextAddLineToPoint(cgContext, 97, 46); CGContextAddCurveToPoint(cgContext, 97, 46, 86, 71, 64, 72); CGContextAddCurveToPoint(cgContext, 42, 74, 26, 56, 23, 48); CGContextAddLineToPoint(cgContext, 9, 47); CGContextAddCurveToPoint(cgContext, 9, 47, 0, 31, 0, 0); ④ CGContextStrokePath(cgContext); } @end
上面代码第①~④行都是在定义这个瓶子的轮廓,其中CGContextAddCurveToPoint函数是定义贝塞尔曲线,函数定义如下。
void CGContextAddCurveToPoint ( CGContextRef c, CGFloat cp1x, //第一控制点 x坐标 CGFloat cp1y, //第一控制点 y坐标 CGFloat cp2x, //第二控制点 x坐标 CGFloat cp2y, //第二控制点 y坐标 CGFloat x, //结束点 x坐标 CGFloat y //结束点 y坐标 );