CodeV

8.7-沿着路径绘制属性文本

沿着路径绘制文本提出了另一种常见的排版难题。图8-29显示了绘制到星形路径上的字符串。为了突出显示输入是属性字符串的事实,在执行任何绘图之前,我随机着色了每个字母。

图8-29

图8-29沿路径排版。

清单8-3计算属性字符串中每个字符的渲染大小,确定边界高度和宽度。获得允许绘图功能确定每个字符(或Core Text术语“glyph”)的路径消耗多少尺寸:

1
2
CGRect bounding = [item boundingRectWithSize:CGSizeMake(
CGFLOAT_MAX, CGFLOAT_MAX) options:0 context:nil];

如果沿着一条线布置,该边界矩形将确定字形的中心的位置。清单8-3使用该距离计算路径长度的百分比,第5章的路径插补(interpolation)返回位置和坡度。正如您在第1章中所看到的,您可以平移和旋转上下文来定位和精确地摆放您的文本。这允许使用NSAttributedStringdrawAtPoint:方法来渲染字符串。

消耗(摆放满)整个路径后,例程停止。这会剪切任何剩余的字符; 他们根本就没有被画(在上面)。如果要确保整个字符串出现,您需要调整字体选择以匹配路径长度。

清单8-3沿着贝塞尔路径放置文本

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
@implementation UIBezierPath (TextUtilities)
- (void) drawAttributedString: (NSAttributedString *) string
{
if (!string) return;
CGContextRef context = UIGraphicsGetCurrentContext();
if (context == NULL) COMPLAIN_AND_BAIL(
@"No context to draw into", nil);

// Check the points
if (self.elements.count < 2) return;

// Keep a running tab of how far the glyphs have traveled to
// be able to calculate the percent along the point path
float glyphDistance = 0.0f;
float lineLength = self.pathLength;

for (int loc = 0; loc < string.length; loc++)
{
// Retrieve the character
NSRange range = NSMakeRange(loc, 1);
NSAttributedString *item =
[string attributedSubstringFromRange:range];

// Start halfway through each character
CGRect bounding = [item boundingRectWithSize:
CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX)
options:0 context:nil];

glyphDistance += bounding.size.width / 2;

// Find new point on path
CGPoint slope;
CGFloat percentConsumed = glyphDistance / lineLength;
CGPoint targetPoint =
[self pointAtPercent:percentConsumed
withSlope:&slope];

// Accommodate the forward progress
glyphDistance += bounding.size.width / 2;
if (percentConsumed >= 1.0f) break;

// Calculate the rotation
float angle = atan(slope.y / slope.x);
if (slope.x < 0) angle += M_PI;

// Draw the glyph
PushDraw(^{
// Translate to target on path
CGContextTranslateCTM(context,
targetPoint.x, targetPoint.y);

// Rotate along the slope
CGContextRotateCTM(context, angle);
// Adjust for the character size
CGContextTranslateCTM(context,
-bounding.size.width / 2,
-item.size.height / 2);

// Draw the character
[item drawAtPoint:CGPointZero];
});
}
}
@end

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