CodeV

5.3-合法和非法路径

合法路径始终以移动操作开始,后跟一系列直线和曲线,并以可选的关闭操作结束。您可以根据需要重复。以下是一个合法路径的总结:

1
path :- ø | {move • (add)* • {add • close | ø}}*

图5.3

路径可以完全为空,如下所示:

1
UIBezierPath *path = [UIBezierPath bezierPath]

或者路径可以包括后面跟着零个或多个添加元素的移动操作。以下代码段通过移动到点p1创建路径,从点p1到点p2(使用控制点c1)添加二次曲线,然后使用从点p2到点p1的线闭合该路径:

1
2
3
[path moveToPoint:p1];
[path addQuadCurveToPoint:p2 controlPoint:c1];
[path closePath];

图5-1(中间图)显示了一个示例,它描绘了这条路径及其move,curve和close元素在绘制时的外观。

关闭元素(如果使用)应始终跟随一个添加直线或添加曲线元素(的操作)。这里,它告诉路径添加从当前点(p2)到第一个点(p1)的直线。

路径状态

贝塞尔曲线运算依赖于路径存储(或更准确地说,能够导出)两个关键的状态段。这些是路径的第一个点被放置的位置和路径的当前点被放置的位置。

例如,当向一个点添加一条线时,完整的线段是[路径的当前点,新的请求点]。当您关闭路径时,您创建的段是[路径的当前点,路径的第一个点]。这些值是随着路径进度而确定的。在每次添加操作时,新的请求点变为当前点:

1
2
3
4
[path moveToPoint:p1]; // First and current points are p1
[path addQuadCurveToPoint:p2
controlPoint:c1]; // Current point is p2
[path closePath]; // No current point

这些状态项很重要,因为每个添加请求都需要至少一个未被方法调用指定的额外状态。这里不考虑addLineFrom:to:. 您或者向某个点添加直线或曲线,或者使用closePath(都需要这个额外状态)。该路径了解当前点在哪里和第一个点在哪里。如果它不能建立这些状态,任何引用它们的新操作是非法的。

只是因为Xcode编译你的代码而不报错和警告并不意味着你正在构建的路径是合法的。例5-1显示了一个非法的路径构造序列。它首先关闭一个路径,然后添加一个二次曲线。

清单5-1使用多个封闭子路径构造路径

1
2
3
4
UIBezierPath *path = [UIBezierPath bezierPath;
[p closePath];
[p addQuadCurveToPoint:p1 controlPoint:p2];
[p stroke];

执行此代码会生成您在此处看到的控制台错误:

1
2
3
<Error>: void CGPathCloseSubpath(CGMutablePathRef): no current point.

<Error>: void CGPathAddQuadCurveToPoint(CGMutablePathRef,const CGAffineTransform *, CGFloat, CGFloat, CGFloat, CGFloat):no current point.

如果你剪切并粘贴到一个项目中,你会发现没有异常,应用程序继续运行。但是,不能绘制路径。即使在示例5-1的最后一行中应用了stroke命令后,当前上下文仍保持不变。

虽然这个特定的例子演示了底层绘图系统的健壮性(robustness),但并不是所有的操作都是如此幸运。某些绘图错误可能会引起干扰应用程序执行的异常。苹果的带有bug的路径反转方法:bezierPathByReversingPath是发生在我身上的致使应用程序崩溃的一个例子。

复合路径

UIBezierPath类允许您将多个路径添加到一起以构建复合路径。如图5-2所示,路径不需要是连续的。它可以包含多个不相交的部分。图5-2的路径包括六个子路径,包括字母的四个外部部分和两个孔(在p和a内部)。

图5-2

图5-2此复合路径由六个子路径组成。

看在代码中创建多个子路径的示例:示例5-2通过一系列move,add和close语句构建新路径。该示例使用两个不同的move - add - close序列,建立具有两个闭合子路径的单个路径。

示例5-2使用多个封闭子路径构造路径

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
UIBezierPath *path = [UIBezierPath bezierPath];

// Begin subpath 1

[path moveToPoint:p1];

[path addLineToPoint:p2];

[path addCurveToPoint:p3 controlPoint1:p4 controlPoint2:p5];

[path closePath];

// Begin subpath 2

[path moveToPoint:p6];

[path addQuadCurveToPoint:p7 controlPoint:p8];

[path addQuadCurveToPoint:p9 controlPoint:p0];

[path closePath];

您可以使用appendPath:附加整个路径。此选项在从现有路径组件组装图形项目时特别有用,如示例5-3中所做。

示例5-3使用多个封闭子路径构造路径

1
2
3
4
5
6
UIBezierPath *path = [UIBezierPath bezierPath];
[path appendPath:[UIBezierPath
bezierPathWithRect:rect1]];

[path appendPath:[UIBezierPath
bezierPathWithOvalInRect:rect2]];

复合路径使您能够构建具有孔和独立元素的复杂图形。在图5-2中,您看到具有六个不同子路径的路径:四个字母的外壳和p和a中的孔。第4章中介绍的偶数/奇数规则确保在填充路径时,这两个孔的内部保持为空,如图5-2所示。


本文翻译自《iOS Drawing Practical UIKit Solutions》作者:Erica Sadun,翻译:Cheng Dong。如果觉得本书不错请购买支持正版:亚马逊购买传送门,本书所有源代码可在GitHub上下载。译者虽然力求做到信,达,雅,但是由于时间仓促加之译者水平十分有限,文中难免会出现不正确,不准确,词不达意,难于理解的地方,还望各位批评指正,共同进步,谢谢。转载请注明出处。