UIBezierPath 贝塞尔曲线的学习

2016-07-02 | 阅读

贝塞尔曲线

贝塞尔曲线是应用于二维图形的数学曲线,由线段和节点组成.节点是可拖动的,线段像可伸缩的皮筋.

贝塞尔曲线一般有三种形式.

  1. 一次曲线,只由一个起始点和一个终止点决定曲线,所以是一条直线.
  2. 二次曲线,由起始点和终止点,加一个控制点决定,控制点影响曲线的形状,如下图所示:

  3. 三次曲线,由起始点加两个控制点决定:

UIBezierPath

使用基础的CGPath的函数比较困难,所以Apple封装了UIBezierPath,通过贝塞尔曲线,更加方便快捷的绘制需要的图形.研究一下提供的接口:

主要的绘图函数

- (void)moveToPoint:(CGPoint)point : 绘图时,有一个起始点,而这个起始点在绘制一条曲线后会自动移动到曲线的终点. 而使用该方法可以直接移动这个起始点, 这个起始点很重要,是之后每次绘图的起点.

- (void)addLineToPoint:(CGPoint)point : 添加一条直线,直线从起始点出发指向终点.

- addCurveToPoint:controlPoint1:controlPoint2: : 添加一个贝塞尔曲线,起始点以当前点,终点以endPoint,并通过两个控制点controlPoint1controlPoint2来控制.

- addQuadCurveToPoint:controlPoint: : 添加一个弧线,只有一个控制点控制.即一个二次的贝塞尔曲线.

- addArcWithCenter:radius:startAngle:endAngle:clockwise: : 画一段圆弧,前面的直线,二次曲线,三次曲线,都算是贝塞尔曲线,但这个圆弧就是单纯的圆弧,不属于贝塞尔曲线了. 由当前起点画一个圆弧. 参数 center 表示圆的原点,radius表示圆的半径,startAngle表示起始的弧度,而endAngle表示结束的弧度,弧度的位置参考下图,clockwise表示画圆的方向是否是顺时针的. 事实上由 原点,半径和弧度就可以决定一个圆弧的位置与形状了,那起点会对画得弧线有什么意义呢? 画弧线之前,会从当前起点做一条直线 连接到圆弧的起点处.

- closePath : 将当前绘制的部分曲线合拢,即用一条直线从当前部分曲线由当前点连接到起点. 当前的部分曲线是指 从上一次 moveToPoint起绘制的曲线.

- removeAllPoints : 清空所有subpath.

- appendPath: : 添加一条曲线.

CGPath : 通过贝塞尔曲线画图后,就可以通过这个属性获取Core Graphics中的曲线.整个曲线是贝塞尔曲线的快照,返回一个不会改变的曲线, 这个对象被 UIBezierPath所持有,直到你再次改变UIBezierPath对象.所以一般使用UIBezierPath绘制图形后,不需要在意CGPath的内存管理.这个属性是可以设置的,所以你也可以自行绘制一个Core Graphic上的CGPath赋值到这里,但是一般不会有人那样做.

lineWidth : 线宽.当设置0的时候,表示当前设备所能设置的最细的线宽,而并不是真的0.但是可能会由于抗锯齿的设置和像素的网格表现原理,最细的线可能并不是一个像素,而是两个像素.

lineCapStyle : 线的端点样式,类型是CGLineCap的枚举,这个枚举有三种值,分别是 :

  • kCGLineCapButt : 表示不绘制端点 ,默认值.
  • kCGLineCapRound : 圆形端点
  • kCGLineCapSquare : 方形端点,与kCGLineCapButt基本没有区别.

lineJoinStyle : 表示线条连接时,连接处的样式.

flatness : 表示绘制曲线的精确度 .详细一点就是 一个曲线上真实的点 与 当前绘制的贝塞尔曲线上的点的最大距离, 以像素为单位, 默认为0.6. 这个值越精确,计算消耗也就越大. 而过于精确本身就没有意义,因为绘制图像都是以像素为单位绘制的,如将精确度提高到 0.001 ,但是在实际像素点上绘制图像,根本无法体现这个精确度.

usesEvenOddFillRule : 使用的填充规则.

- setLineDash:count:phase: : 设置绘制虚线的规则.

- fill : 填充当前绘制的图形,如果填充时有一些子曲线没有合并,该函数会隐式的将这些子曲线给关闭,然后填充, 隐式关闭指不会在子曲线的起点和终点直接显示的添加一条直线.

- fillWithBlendMode:alpha: 填充曲线,并设置填充时的BlendMode 和 透明度.BlendModeCGBlendMode枚举类型决定,表示两个位图混合在一起显示时,对于颜色的渲染的策略.在这里也就是设置添加的贝塞尔曲线要如何在当前的上下文中渲染.

- stroke : 用当前所标记的点 ,绘制出曲线,也就是是填充曲线本身.

- strokeWithBlendMode:alpha: : 以特定的BlendMode和透明度来绘制曲线.

- addClip : 声明之前画的曲线是一个裁剪曲线, 然后之后绘制的图形必须要在裁剪曲线内才能显示. 曲线的范围由usesEvenOddFillRule属性来控制.需要注意的是,如果你在裁剪区域绘制好了图形后,突然又想在裁剪范围外画图时,通过 CGContextSaveGState来储存调用addClip方法之前的绘图上下文的状态,然后在不需要裁剪范围时,使用 CGContextRestoreGState来删除裁剪范围的限制.

- containsPoint: : 调用这个函数来判断点是否在曲线的内部.

bounds : 获取当前Path的范围,涵盖了所有曲线上的点的最小曲线.

UIBezierPath的使用场景

使用场景主要有两处,一处是直接用在drawRect等地方进行画图,一处是绘制出CGPathCAShaperLayer使用.两种情况下使用的方法是不同的.

放在drawRect:中,调用相关方法实际就是直接在CGConextRef上绘图,绘图时,需要调用 :

 // 设置线 颜色
[[UIColor blueColor] setStroke];
// 设置填充色
[[UIColor redColor] setFill];

这两个方法也是直接作用在当前的绘图上下文中的 ,而使用 UIBezierPath的相关方法,也是直接作用在绘图上下文中的.

CAShaperLayer结合使用时, 只是单单作为一个CGPath的构建器来使用,许多与CGConext的方法不能使用,只能简简单单的绘制一个图形. 如 fill , stroke ,addClip 等函数, 这些函数都是CGContext相关的. 而还有些函数的设置,也是无效的,因为CAShaperLayer中有相似的设置,如lineWidth ,lineCapStyle ,lineJoinStyle ,usesEvenOddFillRule ,- setLineDash:count:phase: 等方法和属性,都是无法设置使用的,需要使用CAShaperLayer中的相应属性。