CodeV

5.11-颠倒路径

不幸的是,在iOS 6.x和早期版本的iOS 7 SDK中,特别是使用复杂的bezierPathByReversingPath颠倒Bezier路径的方法已经被打破了。新的解决方案取决于将每个路径分解为子路径(参见清单5-8;每个子路径都以移动操作开始,可能不会以关闭操作结束),然后再分解成组件元素(参见清单5-2)。

一般来说,当您将动画或绘图效果应用于路径表示而不是仅填充或对它描边时,您会想要使用反转路径。例如,如果您使用Bezier路径沿路径布置文本,则颠倒顺序可以更改绘图进程的方式。

翻转路径变得比你想象的有点棘手。这是因为当你颠倒项目,你必须考虑临近点。这意味着反向路径通常看起来像这样:

  • 移动到倒数第二个元素(在close命令之前的那个)的目标点。
  • 向路径的第一个元素添加一直线。
  • 反转每个直线和曲线,使用每个项目的目标点作为起点,将起始元素作为目标。
  • 如果在原始路径中有一个关闭命令,应用该关闭命令以避免奇怪的线帽伪影。

清单5-14显示了冗长的解决方案。它由两种方法组成。公共反转方法将路径分解为子路径,反转这些子路径的顺序。然后它调用私有reverseSubpath:方法,它会翻转每个单独的子路径。

清单5-14创建一个反转的Bezier路径

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
- (UIBezierPath *) reverseSubpath: (UIBezierPath *) subpath
{
NSArray *elements = subpath.elements;
NSArray *reversedElements = [[elements reverseObjectEnumerator] allObjects];

UIBezierPath *newPath = [UIBezierPath bezierPath];
BOOL closesSubpath = NO;

// Locate the element with the first point
BezierElement *firstElement;
for (BezierElement *e in elements)
{
if (!POINT_IS_NULL(e.point))
{
firstElement = e;
break;
}
}

// Locate the element with the last point
BezierElement *lastElement;
for (BezierElement *e in reversedElements)
{
if (!POINT_IS_NULL(e.point))
{
lastElement = e;
break;
}
}

// Check for path closure
BezierElement *element = [elements lastObject];
if (element.elementType == kCGPathElementCloseSubpath)
{
if (firstElement) [newPath moveToPoint:firstElement.point];

if (lastElement) [newPath addLineToPoint:lastElement.point];

closesSubpath = YES;
}
else
{
[newPath moveToPoint:lastElement.point];
}

// Iterate backwards and reconstruct the path
CFIndex i = 0; for (BezierElement *element in reversedElements)
{
i++; BezierElement *nextElement = nil;

BezierElement *workElement = [element copy];

if (element.elementType == kCGPathElementCloseSubpath)
continue;

if (element == firstElement)
{ if (closesSubpath) [newPath closePath]; continue;
}

if (i < reversedElements.count)
{
nextElement = reversedElements[i];
if (!POINT_IS_NULL(workElement.controlPoint2))
{
CGPoint tmp = workElement.controlPoint1;
workElement.controlPoint1 = workElement.controlPoint2;
workElement.controlPoint2 = tmp;
}
workElement.point = nextElement.point;
}

if (element.elementType == kCGPathElementMoveToPoint)
workElement.elementType = kCGPathElementAddLineToPoint;

[workElement addToPath:newPath];
}

return newPath;
}

// Reverse the entire path
- (UIBezierPath *) reversed
{
// [self bezierPathByReversingPath] does not work
// the way you expect. Radars are filed.

UIBezierPath *reversed = [UIBezierPath bezierPath];
NSArray *reversedSubpaths = [[self.subpaths reverseObjectEnumerator] allObjects];

for (UIBezierPath *subpath in reversedSubpaths)
{
UIBezierPath *p = [self reverseSubpath:subpath];
if (p)
[reversed appendPath:p];
}

return reversed;
}

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