Text Programming Guide for iOS - 10 - Lower Level Text-Handling Technologies

底层文本处理技术

大多数应用程序可以使用高级文本显示类和Text Kit来处理所有文本。然而,您可能有一个应用程序需要使用来自Core Text、Core Graphics和Core Animation框架以及其他UIKit API的低级别编程接口。

简单的文本绘制(Simple Text Drawing)

除了用于显示和编辑文本的UIKit类之外,iOS还包括几种直接在屏幕上绘制文本的方法。绘制简单字符串最简单、最有效的方法是使用UIKit对NSString类的扩展,该类位于名为UIStringDrawing的类别中。这些扩展包括使用各种属性在屏幕上任意位置绘制字符串的方法。还有一些方法可以在实际绘制之前计算渲染字符串的大小,这可以帮助您更精确地布局应用程序内容。

重要提示:有一些很好的理由可以避免直接绘制文本,而倾向于使用UIKit框架的文本对象。一个是性能。虽然UILabel对象也绘制其静态文本,但它只绘制一次,而文本绘制例程通常会被重复调用。文本对象还提供了更多的交互;例如,它们是可选择的。

UIStringDrawing的方法在给定的点(对于单行文本)或指定的矩形内(对于多行文本)绘制字符串。您可以传入绘制时使用的属性,例如字体、换行模式和基线调整。UIStringDrawing的方法允许您精确调整渲染文本的位置,并将其与视图的其他内容混合。它们还允许您根据所需的字体和样式属性提前计算文本的边界矩形。

您还可以使用Core Animation的CATextLayer类来进行简单的文本绘制。这个类的对象存储一个普通字符串(plain string)或属性字符串(attributed string)作为其内容,并提供一组影响该内容的属性,如字体、字体大小、文本颜色和截断行为。CATextLayer的优点是(作为CALayer的子类)其属性本质上可以做动画。Core Animation与QuartzCore框架相关联。由于CATextLayer的实例知道如何在当前的图形上下文中绘制自己,所以在使用这些实例时,您不需要发出任何明确的绘制命令。

有关NSString的字符串绘制扩展的信息,请参阅NSString UIKit Additions Reference。要了解有关CATextLayer、CALayer和Core Animation其他类的更多信息,请参阅Core Animation Programming Guide。

Core Text

Core Text是一种用于自定义文本布局和字体管理的技术。应用程序开发人员通常不需要直接使用Core Text。Text Kit建立在Core Text之上,具有相同的优势,例如速度和复杂的排版功能。此外,Text Kit提供了大量的基础设施,如果您使用Core Text,则需要自己构建这些基础设施。

然而,对于必须直接使用它的开发人员来说,Core Text API是可访问的。它旨在由具有自己布局引擎的应用程序使用------例如,具有自己页面布局引擎的文字处理器可以使用Core Text生成字形并将它们放在对的位置。

Core Text作为一个框架实现,发布了一个类似于Core Foundation的API------类似之处在于它是过程式的(ANSI C),但基于类似对象的不透明类型。该API与Core Foundation和Core Graphics都进行了集成。例如,Core Text在许多输入和输出参数中使用Core Foundation和Core Graphics对象。此外,由于许多Core Foundation对象与Foundation框架中的对应对象是"免费桥接"的,因此您可以在Core Text函数的参数中使用这些Foundation对象。

注意:如果您使用Core Text或Core Graphics绘制文本,请记住,您必须对当前文本矩阵应用翻转变换,以使文本以其正确的方向显示------也就是说,绘图原点位于字符串边界框的左上角。

Core Text有两个主要部分:布局引擎和字体技术,每个部分都有自己的不透明类型集合支持。

Core Text布局不透明类型(Core Text Layout Opaque Types)

Core Text需要两个不透明类型不是它本身的对象:一个属性字符串(CFAttributedStringRef)和一个图形路径(CGPathRef)。属性字符串对象封装了显示的字符串,并包括定义字符串中字符的样式方面的属性------例如,字体和颜色。图形路径定义了a frame of text的形状,相当于一个段落。

运行时,Core Text对象形成一个层次结构,这个结构反映了处理的层级(见图9-1)。在这个层次的顶部是framesetter对象(CTFramesetterRef)。framesetter使用属性字符串和图形路径作为输入,生成一个或多个文本frame(CTFrameRef)。文本在frame中布局时,framesetter将段落样式应用于它,包括对齐、制表符、行间距、缩进和换行模式等。

为了生成frame,framesetter会调用一个 typesetter 对象(CTTypesetterRef)。typesetter对象将属性字符串中的字符转换为字形,并将这些字形填充到行中。(字形是用于表示字符的图形形状。)框架中的一行由CTLine对象(CTLineRef)表示。CTFrame对象包含一个CTLine对象的数组。

反过来,CTLine对象包含一个由CTRunRef类型的对象表示的字形runs的数组。字形runs是具有相同属性和方向的连续字形的序列。虽然排版对象返回CTLine对象,但它使用字形运行的数组来组合这些行。

Figure 9-1 Core Text layout objects

使用CTLine不透明类型的函数,您可以从属性字符串中绘制一行文本,而无需通过CTFramesetter对象。您只需将文本的原点定位在文本基线上,并请求行对象自行绘制。

译者注:这段的意思是,CTFrame包含一系列CTLine,一个CTLines包含一系列CTRun,一个CTRun包含一系列具有相同属性和方向的连续字形

Core Text字体不透明类型

字体对于Core Text中的文本处理至关重要。typesetter对象使用fonts(从属性字符串获得的属性)将字符转换成字形,然后排这些字形的相对位置。图形上下文对于Core Text中的字体至关重要。您可以使用图形上下文函数来设置当前字体并绘制字形;或者,您可以从属性字符串创建一个CTLine对象,并使用其函数在图形上下文中进行绘制。Core Text字体系统原生处理Unicode字体。

The font system includes objects of three opaque types: CTFont, CTFontDescriptor, and CTFontCollection:

  • Font objects (CTFontRef) are initialized with a point size and specific characteristics (from a transformation matrix). You can query the font object for its character-to-glyph mapping, its encoding, glyph data, and metrics such as ascent, leading, and so on. Core Text also offers an automatic font-substitution mechanism called font cascading.
  • Font descriptor objects (CTFontDescriptorRef) are typically used to create font objects. Instead of dealing with a complex transformation matrix, they allow you to specify a dictionary of font attributes that include such properties as PostScript name, font family and style, and traits (for example, bold or italic).
  • Font collection objects (CTFontCollectionRef) are groups of font descriptors that provide services such as font enumeration and access to global and custom font collections.

It's possible to convert UIFont objects to CTFont objects by calling CTFontCreateWithName, passing the font name and point size encapsulated by the UIFont object.

Core Graphics文本绘制

Core Graphics(或Quartz)是处理二维图像的系统框架,位于最低级别。文本绘制是其功能之一。一般来说,由于Core Graphics的级别很低,因此建议您使用系统的其他技术来绘制文本。然而,如果情况需要,您可以使用Core Graphics绘制文本。

您可以使用CGContext不透明类型的函数选择字体、设置文本属性并绘制文本。例如,您可以调用CGContextSelectFont来设置所使用的字体,然后调用CGContextSetFillColor来设置文本颜色。然后,您设置文本矩阵(CGContextSetTextMatrix)并使用CGContextShowGlyphsAtPoint绘制文本。

有关这些函数及其使用的更多信息,请参阅Quartz 2D Programming Guide和Core Graphics Framework Reference。

Foundation级别的正则表达式

Foundation框架的NSString类包括一个简单的程序接口来处理正则表达式。您调用三个方法中的一个,该方法返回一个范围,传递一个特定的选项常量和一个正则表达式字符串。如果有匹配项,则该方法返回子字符串的范围。选项是NSRegularExpressionSearch常量,它是位掩码类型的NSStringCompareOptions;这个常量告诉该方法期望一个正则表达式模式,而不是一个字面字符串作为搜索值。支持的正则表达式语法是由ICU(国际Unicode组件)定义的。

注意:除了这里描述的NSString正则表达式特性外,iOS还通过NSRegularExpression类提供了更完整的正则表达式支持。ICU用户指南描述了如何构建ICU正则表达式(userguide.icu-project.org/strings/reg...

NSString的正则表达式方法如下:

rangeOfString:options:

rangeOfString:options:range:

rangeOfString:options:range:locale:

如果您在这些方法中指定了NSRegularExpressionSearch选项,那么您可以指定的其他NSStringCompareOptions选项只有NSCaseInsensitiveSearch和NSAnchoredSearch。如果正则表达式搜索未找到匹配项或正则表达式语法不正确,则这些方法返回一个NSRange结构,其值为{NSNotFound, 0}。

清单9-1给出了使用NSString正则表达式API的示例。

Listing 9-1 Finding a substring using a regular expression

objectivec 复制代码
    // finds phone number in format nnn-nnn-nnnn
    NSRange r;
    NSString *regEx = @"[0-9]{3}-[0-9]{3}-[0-9]{4}";
    r = [textView.text rangeOfString:regEx options:NSRegularExpressionSearch];
    if (r.location != NSNotFound) {
        NSLog(@"Phone number is %@", [textView.text substringWithRange:r]);
    } else {
        NSLog(@"Not found.");
    }
    

由于这些方法为与模式匹配的子字符串返回一个单一的范围值,因此ICU库的某些正则表达式功能要么不可用,要么必须通过编程方式添加。此外,NSStringCompareOptions选项(如向后搜索、数字搜索和不区分重音符号的搜索)不可用,也不支持捕获组。

在测试返回的范围时,您应该意识到基于字面字符串的搜索和基于正则表达式模式的搜索之间存在某些行为差异。一些模式可以成功匹配并返回一个长度为0的NSRange结构(在这种情况下,位置字段是有意义的)。其他模式可以成功匹配空字符串,或者在具有范围参数的方法中,匹配零长度的搜索范围。

这段感觉有点废话文学,就是注意下他提供的函数的返回值和参数

ICU正则表达式支持

如果NSString对正则表达式的支持不能满足您的需求,iOS在系统的BSD(非框架)级别包含了一个ICU 4.2.1库的修改版本。ICU(国际Unicode组件)是一个支持Unicode和软件国际化的开源项目。安装的ICU版本包括支持正则表达式所必需的头文件,以及一些与这些接口相关的修改,即:

parseerr.h

platform.h

putil.h

uconfig.h

udraft.h

uintrnal.h

uiter.h

umachine.h

uregex.h

urename.h

ustring.h

utf_old.h

utf.h

utf16.h

utf8.h

utypes.h

uversion.h

您可以在icu-project.org/apiref/icu4... 4.2 API文档和用户指南。

简单的文本输入

想要显示和处理文本的应用程序不仅限于UIKit框架的文本和web对象。它可以实现自定义视图,这些视图可以从简单的文本输入到复杂的文本处理和自定义输入。通过这些可用的编程接口,这些应用程序可以获得自定义文本布局、多阶段输入、自动校正、自定义键盘和拼写检查等功能。

您可以实现自定义视图,允许用户在插入点输入文本,并在点击删除键时删除插入点之前的字符。例如,即时消息应用程序可以有一个视图,允许用户输入他们的谈话内容。

您可以通过子类化UIView或任何其他继承自UIResponder的视图类,并采用UIKeyInput协议,获得简单文本输入的能力。当您的视图类的实例成为第一响应者时,UIKit将显示系统键盘。UIKeyInput本身采用了UITextInputTraits协议,因此您可以设置键盘类型、返回键类型和键盘的其他属性。

注意:只有一部分语言和输入法在你仅仅采用UIKeyInput协议的时候就可用了。例如,任何多阶段输入法,如中文、日文、韩文和泰文是不行的。还需要采用了UITextInput协议。

要采用UIKeyInput,您必须实现它声明的三个方法:hasText、insertText:和deleteBackward。要实际绘制文本,您可以使用本章中总结的任何技术。然而,对于简单的文本输入,例如在自定义控件中的单行文本,UIStringDrawing和CATextLayer API是最合适的。

清单9-2说明了自定义视图类的UIKeyInput实现。本例中的textStore属性是一个NSMutableString对象,用作文本的后台存储。该实现要么在字符串末尾追加字符,要么删除字符串中的最后一个字符(取决于按下的是字母数字键还是删除键),然后重绘textStore。

Listing 9-2 Implementing simple text entry

objectivec 复制代码
- (BOOL)hasText {
    if (textStore.length > 0) {
        return YES;
    }
    return NO;
}
 
- (void)insertText:(NSString *)theText {
    [self.textStore appendString:theText];
    [self setNeedsDisplay];
}
 
- (void)deleteBackward {
    NSRange theRange = NSMakeRange(self.textStore.length-1, 1);
    [self.textStore deleteCharactersInRange:theRange];
    [self setNeedsDisplay];
}
 
- (void)drawRect:(CGRect)rect {
    CGRect rectForText = [self rectForTextWithInset:2.0]; // custom method
    [self.theColor set];
    UIRectFrame(rect);
    [self.textStore drawInRect:rectForText withFont:self.theFont];
}

为了在实际视图中绘制文本,这段代码使用了NSString的UIStringDrawing类别中的drawInRect:withFont:方法。

与文本输入系统通信

iOS的文本输入系统管理键盘。它将点击解释为特定键盘中特定键的按下,这些键盘适合某些语言。然后,它将相关字符发送到目标视图以插入。如简单文本输入中所述,视图类必须采用UIKeyInput协议来在插入点插入和删除字符。

然而,文本输入系统所做的不仅仅是简单的文本输入。例如,它管理自动校正和多阶段输入,这些都是基于当前选择和上下文的。多阶段文本输入对于表意文字语言(如汉字(日语)和汉字(汉语))是必需的,这些语言从音标键盘输入。要获得这些功能,自定义文本视图必须通过采用UITextInput协议并实现相关的客户端类和协议来与文本输入系统通信。

剩下的部分建议去看原文章,我感觉咋翻译都拗口。

相关推荐
JarvanMo1 小时前
flutter工程化之动态配置
android·flutter·ios
彩旗工作室2 小时前
iOS应用开发指南
ios
season_zhu11 小时前
iOS开发:关于Model
ios·架构·swift
异次元客14 小时前
选择设备对象进行图形渲染
ios·apple
iOS大前端海猫1 天前
Swift 中的async和await
ios·编程语言
Superxpang1 天前
JavaScript `new Date()` 方法移动端 `兼容 ios`,ios环境new Date()返回NaN
开发语言·前端·javascript·ios·typescript·安卓
iOS大前端海猫2 天前
Swift 中重要特性——逃逸闭包@escaping
前端·ios
亮亮哥2 天前
设计一种机制检测UIViewController的内存泄漏
ios
异次元客2 天前
使用渲染管线渲染图元
ios·apple