CodeV

8.8-文本拟合

有几个UIKit类都包括size-to-fit选项。 但是,没有通用的字符串绘制任务的等效功能。您可以找到一个字符串,将字符串“拟合”绘制到目的地。 以矩形为例。 如果字体足够小,几乎任何文本都将“拟合”成矩形。 不幸的是,这导致了非常小的单行文本。 挑战是找到最大的字体来换行以匹配目标,所以绘制的文本的轮廓尺寸对应于该矩形的轮廓尺寸。 图8-30显示不同长度的文本串。 每个绘图都拟合其目的地,通过调整字体的大小,同时测试以拟合轮廓的尺寸。

图8-30

图8-30字体拟合。

清单8-4详细介绍了图8-30中用来创建输出的算法。 这不是一个精确的系统,因为我通过tolerance参数添加了一些灵活性。 因此,这种实现有时可能会略微超出目的地。所以,请考虑使用比实际使用的稍微小一点的目的地调用该函数。

该函数迭代地选择一个字体。 它测试其输出大小,并且当输出的轮廓尺寸接近目标矩形的轮廓尺寸时,算法停止并返回最近成功(拟合)的字体。

与本章中的所有其他材料一样,清单8-4已更新为使用iOS 7调用。 检索文本边界框的新方法无法部署到iOS 7以前的系统。

清单8-4选择拟合字体

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
UIFont *FontForWrappedString(
NSString *string, NSString *fontFace,
CGRect rect, CGFloat tolerance)
{
if (rect.size.height < 1.0f) return nil;

CGFloat adjustedWidth = tolerance * rect.size.width;
CGSize measureSize =
CGSizeMake(adjustedWidth, CGFLOAT_MAX);

// Initialize the proposed font
CGFloat fontSize = 1;
UIFont *proposedFont =
[UIFont fontWithName:fontFace size:fontSize];

NSMutableParagraphStyle *paragraphStyle =
[[NSMutableParagraphStyle alloc] init];
paragraphStyle.lineBreakMode = NSLineBreakByWordWrapping;

NSMutableDictionary *attributes =
[NSMutableDictionary dictionary];
attributes[NSParagraphStyleAttributeName] = paragraphStyle;
attributes[NSFontAttributeName] = proposedFont;

// Measure the target
CGSize targetSize =
[string boundingRectWithSize:measureSize
options:NSStringDrawingUsesLineFragmentOrigin
attributes:attributes context:nil].size;

// Double until the size is exceeded
while (targetSize.height <= rect.size.height)
{
// Establish a new proposed font
fontSize *= 2;
proposedFont =
[UIFont fontWithName:fontFace size:fontSize];

// Measure the target
attributes[NSFontAttributeName] = proposedFont;
targetSize =
[string boundingRectWithSize:measureSize
options:NSStringDrawingUsesLineFragmentOrigin
attributes:attributes context:nil].size;

// Break when the calculated height is too much
if (targetSize.height > rect.size.height) break;
}

// Search between the previous and current font sizes
CGFloat minFontSize = fontSize / 2;
CGFloat maxFontSize = fontSize;
while (1)
{
// Get the midpoint between the two
CGFloat midPoint = (minFontSize +
(maxFontSize - minFontSize) / 2);
proposedFont =
[UIFont fontWithName:fontFace size:midPoint];
attributes[NSFontAttributeName] = proposedFont;
targetSize =
[string boundingRectWithSize:measureSize
options:NSStringDrawingUsesLineFragmentOrigin
attributes:attributes context:nil].size;

// Look up one font size
UIFont *nextFont =
[UIFont fontWithName:fontFace size:midPoint + 1];
attributes[NSFontAttributeName] = nextFont;
CGSize nextTargetSize =
[string boundingRectWithSize:measureSize
options:NSStringDrawingUsesLineFragmentOrigin
attributes:attributes context:nil].size;

// Test both fonts
CGFloat tooBig = targetSize.height > rect.size.height;
CGFloat nextIsTooBig =
nextTargetSize.height > rect.size.height;

// If the current is sized right
// but the next is too big, it’s a win
if (!tooBig && nextIsTooBig)
return [UIFont fontWithName:fontFace size:midPoint];

// Adjust the search space
if (tooBig)
maxFontSize = midPoint;
else
minFontSize = midPoint;
}

// Should never get here
return [UIFont fontWithName:fontFace size:fontSize / 2];
}

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