用Core Text创建简单杂志应用(1)

原文:http://www.raywenderlich.com/4147/how-to-create-a-simple-magazine-app-with-core-text

本文作者Marin Todorov,拥有12年以上的软件开发经历,是一位独立的iOS开发者,同时是TouchCode 杂志的创建者。


Core Text是iOS 3.2和OSX10.5之后的文本引擎,能够精确控制文本的布局及样式。

它位于UIKit和CoreGraphics/Quartz之间:

  •  使用UIKit,你可以通过简单拖拽将文本显示在UILabel上,但你无法单独控制文本中每个字的颜色。
  • 使用Core Graphics/Quartz,你可以做到系统能够做到的一切,但你需要计算文本每个字符绘制在屏幕上的坐标。
  • Core Text 恰巧位于二者之间。你能完全控制文字的位置、布局以及颜色大小等属性,但Core Text也为你省去了一些工作——例如从文本换行到字体的渲染。

在创建一个杂志或书刊类的应用时,Core Text尤其便利——而且在iPad上也工作得很好。

本文通过创建一本非常简单的杂志应用教你如何使用CoreText。你将学到:

  • 在屏幕上显示格式文本
  • 精确控制文本的外观
  • 在文本内容中插入图片
  • 最终创建一个杂志应用,并通过标记来控制文本的格式

在阅读本文之前,你需要有一点iOS开发基础。否则,你可以先阅读这个站点的其他教程。

让我们立即开始iPad杂志之旅。

创建 Core Text 项目

打开Xcode,点击 File\New\New Project, 选择 iOS\Application\View-basedApplication, 点击 Next。项目名称命名为 CoreTextMagazine, Device family选择iPad, 点击 Next, 选择项目存放路径,然后点击 Create。

接下来将Core Text框架加到项目中。

Next thing you have to do is add the Core Text framework to theproject:

  1. 在 Project  导航面板中选择项目文件 (屏幕左边的导航窗口)
  2. 在Targets列表中选择 “CoreTextMagazine”
  3. 点击“Build phases” 栏
  4. 展开 “Link Binary With Libraries” 并点击 “+” 号按钮
  5. 在列表中选择 “CoreText.framework” 然后点击 “Add”按钮

接下来编写一些代码。

添加一个 Core Text 视图

为了便于使用Core Text,我们应当创建一个定制的UIView,然后在它的drawRect:方法中使用Core Text。

打开File\New\New File菜单,选择 iOS\Cocoa Touch\Objective-C class, 点击 Next。选择UIView的子类, 点击 Next, 类名命名为 CTView, 然后点击 Save。

 

在CTView.h 的 @interface 加入一下代码已包含 Core Text 框架:

#import <CoreText/CoreText.h>

接下来,设置新的定制的UIView作为应用程序的主视图。

在项目导航窗口中选择 “CoreTextMagazineViewController.xib”文件,转到Xcode的 Utilities 工具条,选择第3个按钮即 Identity 栏.

现在界面编辑器中选择view对象,在Utilities窗口的Class栏,可以看到UIView字样,将它改为 “CTView” 然后回车。

现在,应用程序启动时将显示定制的Core Text视图了。接下来,我们编写一点代码作为测试。

打开CTView.m 删除所有方法。输入以下代码,以便在视图中绘制一个内容为 “Hello world” 的文本:

- (void)drawRect:(CGRect)rect {
     [super drawRect:rect];
     CGContextRef context = UIGraphicsGetCurrentContext();
     CGMutablePathRef path = CGPathCreateMutable(); //1
     CGPathAddRect(path, NULL, self.bounds );
     NSAttributedString* attString = [[[NSAttributedString alloc]         initWithString:@"Hello core text world!"] autorelease]; //2
     CTFramesetterRef framesetter =         CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attString); //3
     CTFrameRef frame =         CTFramesetterCreateFrame(framesetter,             CFRangeMake(0, [attString length]), path, NULL); 
     CTFrameDraw(frame, context); //4
     CFRelease(frame); //5
     CFRelease(path);
     CFRelease(framesetter); 
}

让我们逐句讨论以上代码(序号和代码后面注释的行号对应):

  1. 此句创建了一个路径,用于表示文本将绘制在此区域内。Mac下Core Text支持在各种形状中绘制文本,比如矩形和椭圆。但iOS下仅支持矩形。在本例中,使用整个视图作为文本绘制的路径,直接将self.bounds转变为一个CGPath。
  2. 在Coret Text中无法直接使用NSString,而是使用NSAttributedString。NSAttributedString是一个强大的NSString子类,允许你在文本上应用各种格式化属性。待会我们会演示如何使用它,在此我们仅仅是创建一个简单的文本。
  3. CTFramesetter是绘制Core Text文本的最重要的一个类。用它来管理所有的字体引用和文本绘制块。现在你需要知道的仅仅是使用 CTFramesetterCreateWithAttributedString函数创建一个 CTFramesetter。在创建出CTFramesetter之后,我们可以用 CTFramesetterCreateFrame 给它指定一个渲染范围(这里我们决定渲染范围为整个字符串),以及绘制文本时文本显示范围,方法最终返回一个文本绘制块。
  4. CTFrameDraw在指定上下文中绘制指定的文本绘制块。
  5. 最后,释放所用到的对象。

注意,在使用Core text时,请使用一系列函数如 CTFramesetterCreateWithAttributedString 和CTFramesetterCreateFrame 而尽量避免直接使用 Objective-C 对象。

你可能会奇怪“为什么又要用C,为什么不用Objective-C?”

这是因为在iOS中的许多低级类库,为了高效和简洁起见,都是用C写的。不必担心,你会发现CoreText函数其实蛮简单的。

此外,还有一件值得注意的事情:在用“Create”函数创建对象之后,不要忘记同时要用CFRelease函数释放它们。

无论如何,用Core Text绘制一段简单文本就是这么多了。点击Run,查看运行结果。


出现了什么问题?就好比许多低级APIs,CoreText使用的是反Y轴坐标系。因此文本内容被渲染成倒立的。我们需要记住一点,如果我们把UIKit和Core Text的绘制方向搞混了,你会得到奇怪的结果。让我们来解决这个问题。在

 “CGContextRef context =UIGraphicsGetCurrentContext();”一行后加入一下代码:

// Flip the coordinate system
CGContextSetTextMatrix(context, CGAffineTransformIdentity);
CGContextTranslateCTM(context, 0, self.bounds.size.height);
CGContextScaleCTM(context, 1.0, -1.0);

代码很简单,仅仅是在当前视图的上下文中应用了一个转换(上下颠倒坐标系)。在每次绘制Core Text的时候复制粘贴这段代码。

点击Run——庆祝你的第一个CoreText程序吧!


Core Text 的对象模型

如果你搞不清什么是 CTFramesetter 和 CTFrame – 那么,我会在这里简单地解释一下Core Text是如何渲染文本内容的。

Core Text的对象模型如下图所示:

用Core Text创建简单杂志应用(1)_第1张图片

首先用一个NSAttributedString做为参数创建一个CTFramesetter引用。这将自动创建一个CTTypesetter实例,它管理着你的字体。接下来使用这个CTFramesetter创建一个(或多个)文本块(CTFrame)。CTFrame用于渲染文本。

在创建文本块时,需要告诉它一个范围(NSRange),指定文本将在某个矩形内进行渲染。Core Text为每行文字自动创建一个CTLine,同时(注意这里)为每个CTLine中的每一段格式相同的文字创建一个CTRun。

例如,如果在一行中,有几个字是红色,另外几个字是黑色,剩余的字则为粗体,则Core Text将总共为它们创建3个CTRun。特别注意:不需要你来创建CTRun属性,Core Text会基于你提供的NSAttributedString来创建它们。

每个CTRun对象都采用各自的属性,因此你可以精确地控制诸如字距、连体、字宽、字重等属性。

创建杂志应用程序

创建杂志应用程序,我们需要标记出每段文字的属性。我们可以用NSAttributedString的setAttributes:range方法,但未免不够灵活(除非你愿意老老实实地写上成打的代码)。

更简单地方法,是创建一种简单标记语法的解析器,通过它对杂志内容进行格式化。

点击File\New\New File, 选择 iOS\Cocoa Touch\Objective-C class, 然后点击Next。选择 NSObject for Subclass of, 点击 Next,为类命名为MarkupParser.m, 然后点击 Save。

在MarkupParser.h 中删除所有代码,编辑代码,声明属性和方法如下:

#import <Foundation/Foundation.h> 
#import <CoreText/CoreText.h>   
@interface MarkupParser : NSObject {
     NSString* font;
     UIColor* color;
     UIColor* strokeColor;
     float strokeWidth;
     NSMutableArray* images; 
}   
@property (retain, nonatomic) NSString* font; 
@property (retain, nonatomic) UIColor* color; 
@property (retain, nonatomic) UIColor* strokeColor; 
@property (assign, readwrite) float strokeWidth;   
@property (retain, nonatomic) NSMutableArray* images;   
-(NSAttributedString*)attrStringFromMarkup:(NSString*)html;   
@end

打开MarkupParser.m 将其中内容替换为以下代码:

#import "MarkupParser.h"   
@implementation MarkupParser   
@synthesize font, color, strokeColor, strokeWidth; 
@synthesize images;   
-(id)init { 
    self = [super init]
    if (self) { 
        self.font = @"Arial"; 
        self.color = [UIColor blackColor]
        self.strokeColor = [UIColor whiteColor]; 
        self.strokeWidth = 0.0;   
        self.images = [NSMutableArray array];     
    }
    return self; 
}   
-(NSAttributedString*)attrStringFromMarkup:(NSString*)markup {   }   
-(void)dealloc {
     self.font = nil;
     self.color = nil;
     self.strokeColor = nil;
     self.images = nil;  
     [super dealloc]; 
}   
@end

如你所见,这个解析器的代码很简单——它包含的属性只有字体、字体颜色、删除线粗细、删除线颜色。后面我们将在文本中使用图片,因此用一个数组来存储这些图片。

通常写一个解析器不是件轻松的活儿,我将演示的是教你编写一个非常非常简单的解析器(使用正则式)。本教程的解析器非常简单,仅支持开放标签——例如,从一个标签后开始设置文本的样式,一直应用这个样式直到遇到新的标签。就像这段标记语言:

These are <font color="red">red<font color="black"> and <font color="blue">blue <font color="black">words. 

将产生如下输出:

These are red and blue words. 

对于本文而言,这就足够了。在你自己的项目中,你可以扩展它。

让我们开始吧!

在attrStringFromMarkup: 方法中,加入:

NSMutableAttributedString* aString =
     [[NSMutableAttributedString alloc] initWithString:@""]; //1   
NSRegularExpression* regex = [[NSRegularExpression alloc]
   initWithPattern:@"(.*?)(<[^>]+>|\\Z)"
           options:NSRegularExpressionCaseInsensitive|
                   NSRegularExpressionDotMatchesLineSeparators
             error:nil]; //2 
NSArray* chunks = [regex matchesInString:markup
       options:0
         range:NSMakeRange(0, [markup length])];
[regex release];

以上代码分为两步:

  1. 首先,声明一个空字符串的NSMutableAttributeString用于结果的返回,后面我们会加入搜索到的匹配的字符串。
  2. 接下来,创建一个正则表达式,用于匹配文本和标记。该正则表达式用于匹配一段文字以及其后的标记。该正则表达式的意思是“查找一系列字符,一直到遇到一个以左尖括号开始、右尖括号结束的任意个字符。如果遇到字符串结束则停止处理”。.

为什么要创建这个正则表达式?我们用它来搜索字符串中每个匹配的位置,然后:

1)渲染所找到的文本部分;

2)根据所找到标签改变当前样式。

整个过程重复进行,直至文本结束。

确实很简单,不是吗?



你可能感兴趣的:(用Core Text创建简单杂志应用(1))