CodeV

8.5-使用Core Text绘制

iOS 7的Text Kit大大侵入了传统上与Core Text相关的高端排版技术。Text Kit提供了一个基于Objective-C的类和协议,为UITextView提供排版功能。 相比之下,Core Text是一个基于C的API。 排版功能可以直接绘制到上下文中。 如图8-3所示,Text Kit尚未完全成熟化。因此,本节涵盖Core Text而不是Text Kit。

在Core Text中,Bezier路径使您能够将文本绘制到您指定的任何形状或布局。图8-22显示了一个简单的例子,此图像在星形状中使用Core Text布局。

图8-22

图8-22将属性文本绘制到路径中。文字沿路径边缘被包围。

图8-22使用字符换行,因此部分单词在行之间被分割,但是布局中不会有字符或单词丢失,如果您使用addClip并绘制到形状的边界,那么它们将是如此。 所有文本都显示在路径中,但被包裹到周围的形状(but is wrapped to the surrounding shape.)。

通常情况下,您不会将文字画成如此奇怪的形状。我这样做是因为我想演示清单8-1中的功能。 它接受路径和属性字符串,并将该字符串绘制到该路径中。它由Core Text框架设置者提供,这是一个用于构建绘制文本的容器(“框架”)的对象。

Core Text框架提供强大的文本布局和字体管理功能集。针对在文本处理空间中工作的应用程序,Core Text包括诸如框架设置器之类的工具,可让您定义绘制复杂字符串布局的几何目的地。 这些布局遵循通过属性字符串应用的段落样式,如对齐,tab stops,行间距和缩进。

得益于Core Text框架,所有绘图都在Quartz空间中进行。 您提供的CGPath也必须在Quartz几何中定义。这就是为什么我选择一个非垂直对称的图8-11的形状。清单8-1通过复制您提供的路径并在上下文中垂直镜像该副本来处理此绘图问题。没有这个步骤,最终得到如图8-23所示的结果:文本仍然从上到下,但路径使用的是Quartz坐标系。

图8-23

图8-23Core Text预期路径使用Quartz坐标系。

清单8-1将属性字符串绘制到路径容器中

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
void DrawAttributedStringInBezierPath(
UIBezierPath *path,
NSAttributedString *attributedString)
{
CGContextRef context = UIGraphicsGetCurrentContext();
if (context == NULL)
COMPLAIN_AND_BAIL(
@"No context to draw into", nil);

// Mirror a copy of the path
UIBezierPath *copy = [path safeCopy];
MirrorPathVerticallyInContext(copy);

// Build a framesetter and extract a frame destination
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(
(__bridge CFAttributedStringRef) attributedString);
CTFrameRef theFrame = CTFramesetterCreateFrame(
framesetter, CFRangeMake(0, attributedString.length),
copy.CGPath, NULL);

// Draw into the frame
PushDraw(^{
CGContextSetTextMatrix(context, CGAffineTransformIdentity);
FlipContextVertically(GetUIKitContextSize());
CTFrameDraw(theFrame, UIGraphicsGetCurrentContext());
});

CFRelease(theFrame);
CFRelease(framesetter);
}

// Flip the path vertically with respect to the context
void MirrorPathVerticallyInContext(UIBezierPath *path)
{
CGContextRef context = UIGraphicsGetCurrentContext();
if (context == NULL) COMPLAIN_AND_BAIL(
@"No context to draw into", nil);

CGSize size = GetUIKitContextSize();
CGRect contextRect = SizeMakeRect(size);
CGPoint center = RectGetCenter(contextRect);

// Flip path with respect to the context size
CGAffineTransform t = CGAffineTransformIdentity;
t = CGAffineTransformTranslate(t, center.x, center.y);
t = CGAffineTransformScale(t, 1, -1);
t = CGAffineTransformTranslate(t, -center.x, -center.y);
[path applyTransform:t];
}

文本矩阵

文本矩阵定义单独应用于每个字符而不是整个上下文的变换。它们被存储为每个上下文的GState的一部分。 当混合使用UIKit和Core Text绘图时,请确保重新设置Core Text调用中的文本矩阵,如清单8-1所示,在其PushDraw()块中所做的。

我们来看看你如何执行更新。 以下调用会重置用于将字符串呈现到上下文中的任何文本转换:

CGContextSetTextMatrix(context, CGAffineTransformIdentity);

要了解为什么必须执行此步骤,请看图8-24。 该图中的文本被绘制两次,首先使用UIKit属性字符串绘图,然后使用Core Text。Core Text输出显示为颠倒。每个字母都是垂直的单独反射。

图8-24

图8-24将文本绘制成复杂的贝塞尔路径可能会产生意想不到的结果。

这个奇怪的效果是因为UIKit字符串绘图改变了上下文的文本矩阵。您将在示例8-5中看到这种情况是如何发生的。

示例8-5将属性字符串绘制到路径中

1
2
3
4
5
6
7
8
9
10
11
12
13
UIGraphicsBeginImageContextWithOptions(size, NO, 0.0);
CGAffineTransform t;

// Retrieve the initial text matrix
t = CGContextGetTextMatrix(UIGraphicsGetCurrentContext());
NSLog(@"Before: %f", atan2f(t.c, t.d));

// Draw the string
[string drawInRect:targetRect];
// Retrieve the changed text matrix
t = CGContextGetTextMatrix(UIGraphicsGetCurrentContext());
NSLog(@"After: %f", atan2f(t.c, t.d));
UIGraphicsEndImageContext();

您可能希望这两个日志语句都不会显示为旋转。 相反,这是实际发生的事情。 用于支持UIKit字符串绘制的最终180度旋转解释了图8-24所示的输出:

1
2
2013-05-10 09:38:06.434 HelloWorld[49888:c07] Before: 0.000000
2013-05-10 09:38:06.438 HelloWorld[49888:c07] After: 3.141593

不幸的是,您不能通过保存和恢复上下文的图形状态来解决此问题。 根据苹果描述,“请注意,文本矩阵不是图形状态的一部分 - 保存或恢复图形状态对文本矩阵没有影响。 文本矩阵是图形上下文的属性,而不是当前字体的。“

相反,当您切换到Core Text绘图时,您需要显式地重置文本矩阵,如清单8-1所示:

CGContextSetTextMatrix(context, CGAffineTransformIdentity);


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