关于iOS的文字处理
UIKit提供app的文字和网页对象,可以直接使用UItextView,TextField,Label。当用户编辑文字时,app必须管理键盘。当用户点击textfile,textview或者网页上的表单,iOS动态弹出键盘到视图。App可以直接绘制和管理文字。UIkit的底层text view是一个强大的布局引擎——Text Kit. 如果你需要定制布局过程或者需要干预这个过程的行为,你可以使用Text Kit。Text Kit是一组类和协议,提供了高质量的排版服务,允许app用所有良好的排版设置来存储,布局,显示文字。比如:kerning, ligatures, line breaking, and justification.
对于大多数app来说,你可以使用high-level 文字显示类和text kit来处理所有文字场景。对于需要定制解决方案的少数文本和特殊需求,可以使用底层技术,例如Core Text,Core Graphics和Core Animation框架中的编程接口以及UIKit中的其他API。
要直接与iOS的文本输入系统通信,请实现UITextInput协议以及相关的协议和类。您的应用程序还可以使用拼写检查和正则表达式的技术。
你的应用程序具有一系列用于输入和编辑数据的选项。UIKit框架包含编程接口,用于编辑视图中的数据并将数据输入到应用程序中。自定义输入视图可以替换系统键盘以允许输入特殊数据;输入附件视图是系统键盘(或自定义输入视图)上方的自定义视图,可使用户以特定于应用程序的方式影响编辑的数据。通过使用UIPasteboard和相关类,应用程序可以在自己的不同位置或与其他应用程序之间复制,剪切和粘贴数据。作为复制粘贴操作的一部分,用户在上下文编辑菜单上点击命令;你的应用程序管理这个菜单,并可以添加自定义命令。
排版的概念 Typographical Concepts
字符和字形 Characters and Glyphs
字符是带有意义的书面语言的最小单位。 字符可以与语言的口语形式中的特定声音相对应,如罗马字母的字母; 他们可以代表整个单词,如汉字; 或者他们可以表示独立的概念,如数学符号。 然而,在任何情况下,字符都是一个抽象的概念。
尽管字符必须以可识别的形状在显示区域中表示,但它们与该形状不同。 也就是说,字符可以以各种形式绘制并保持相同的特征。 例如,可以使用不同大小或不同笔触粗细绘制“大写字母A”字符,它可以倾斜或垂直,并且可以在形式上具有某些可选的变体,例如衬线。 任何一种字符的各种具体形式都被称为字形。
字样和字体 Typefaces and Fonts
字样Typefaces是书面语言中一些或全部字符的一组视觉相关的形状。例如,“times”是一个字体,由Stanley Morrison于1931年为伦敦泰晤士报撰写。 “times”的所有字母都外表相关,在垂直笔画和字母体中的弧度和其他元素之间具有一致的比例。当按照文本块排列时,字样中的形状一起工作以增强可读性。
字体风格TypestyleSimpleStyle风格是字体的独特视觉特征。例如,Roma字体的特征是直线字母,其衬线和字根比水平线粗。在意大利斜体字体中,字母向右倾斜并且是圆形的,类似于草书或手写字母形状。字体通常有几种相关的类型。
字体Font是一系列字形,它们描述的字符大小一致,字体和字体风格一致。字体旨在用于特定的显示环境。字体包含所有上下文形式的字形,如连字,以及普通字符形式。
字体系列FontFamily是一组共享字体但字体风格不同的字体。所以,例如,Times是一个字体系列的名称(以及它的字体名称)。时代罗马和时代斜体是属于时代家族的两个单独字体的名称。
以上概念就是传统印刷行业对字体字形相关的使用和定义。在计算机处理上,实际和传统印刷行业没什么不同。
使用TextKit绘制和管理文字 Using Text Kit to Draw and Manage Text
UIKit提供了非常多的文字处理页面来处理文字,比如:UITextView, UITextField, and UILabel。他们共同的相似之处就是使用TextKit这个排版引擎来处理文字。TextKit 是CoreText的上层封装。如图:

Text Kit可让您完全控制用户界面元素中的文本呈现。除了UITextView之外,UITextField和UILabel构建在Text Kit之上,并且与动画,UICollectionView和UITableView无缝集成。 Text Kit的设计具有完全可扩展的面向对象体系结构,支持子类化,delegate以及一套完整的通知,从而实现深度定制。
主要TextKit 对象Primary Text Kit Objects
TextKit对象的数据流如图,Text Views是UITextView的实例,text containers 是NSTextContainer的实例,layout manager是NSLayoutManager的实例,text storage 是NSTextStorage的实例。在Text Kit中,NSTextStore对象存储显示在UITextView对象中的文字,该文字倍NSLayoutManager对象布局到一个NSTextContainerdingyi的区域中。
 
NSTextContainer对象定义一个文字可以被布局的区域。通常情况,一个text Container定义一个矩形区域。但是通过创建NSTextContainer的子类可以创建其他形状:圆,五角形或者不规则图形,例如:TextContainer不仅描述了可以用文本填充的区域的轮廓,还保留了一系列Bezier Path,这些路径是其区域中未排列文本的排除区域。 按照布局,text 围绕着exclusion path,提供了一种方法来包含图形和其他非文本布局元素。
NSTextStorage 是Text Kit的基础存储机制。NSTextStorage 是 NSMutableAttributedString的子类,存储文字系统的字符和属性。他确保文本和属性在编辑操作中保持一致状态。为了存储文字,NSTextStorage对象管理一组(set)NSLayoutManager对象,当任它们的字符或者属性变化时通知它们,所以如果需要的话,这些layout manager可以延迟或者重新显示文字。
NSLayoutManager对象编排其他文本处理对象的操作。它在将NSTextStorage对象中的数据转换为视图显示区域中的呈现文本的操作中进行了干预(操作)。他映射Unicode Character codes到字形,监视字形在NSTextContainer区域里的布局。
Note: NLayoutManager, NSTextStorage, and NSTextContainer 可以在自线程访问(只要应用程序保证从单个线程访问)
文字属性 Text Attributes
Text Kit 处理三种文字属性:Character attributes/Paragraph attributes/Document attributes,字符属性包含诸如font,color等特征,还有subscript(下标)——关联一段字符中的某个独立字符。段落属性是indentation缩进, tabs跳格, and line spacing行距。文档属性包含文档全局的特征,比如:Paper Size,margins,view zoom percentage。
文字属性主要的对象有Text Style,Font Descriptor 等,TextStyle使用了OC的动态类型特性,不同的Text Style代表了不同的文字风格,比如可以用于title的属性集合,用于正文的属性集合等。
FontDescriptor是针对Font的精细操作。
布局Text Laying Out Text
Layout manager 对象,实例化子NSLayoutManager,是TextKit显示文字的核心。Layout manager 实现以下操作:
  • 控制文字存储和text container 对象
  • 从字符生成字形
  • 计算字形位置,存储相关信息
  • 管理字形和字符的范围
  • 在TextView里绘制字形(requested by view)
  • 计算文字行lines的box 矩形范围
  • 控制连字断字
  • 操作字符和字形属性
在MVC模式中,Layout manager 是一个controller,NSTextStorage(一个NSMutableAttributedString的子类)提供部分model,持有一个有属性特征(如typeface,style,color和size)的文字字符集,NSTextContainer也可以认为是部分的model,因为他管理了页面文字的几何布局。UItextView(or UIView)提供了文字显示的视图。NSLayoutManager作为controller,服务文字系统(因为他翻译字符为字形到Text storage,在行lines中布局他们,根据一个或多个Text Container对象的尺寸,并将文本显示配合到一个或多个文本视图对象中)。
Layout 的过程
layout manager 的文字布局分两步:字形生成;字形布局。布局管理员Lazyliy执行两个布局步骤,即根据需要执行。因此,一些NSLayoutManager方法会导致字形生成,而另一些则不会,字形布局也是如此。在NSLayoutManager生成字形、计算完布局位置后,layout manager缓存信息来提高后续调用的性能。
layout manager缓存字形,属性和布局信息。一直追踪通过更改text storage中的字符而失效的字形范围。有两种方法可以自动使字符范围失效:如果需要生成字形,或需要layout字形。如果您愿意,可以手动使字形或布局信息无效。当layout manager收到需要生成字形或布局的消息时,它会生成字形或根据需要重新计算布局。
生成分段矩形
Layout manager在NFTextContainer对象中以文字行形式放置文本。Text Container中这些行的布局由其形状和其包含的任何排除路径决定。无论线段矩形与排除路径定义的区域相交,这些部分中的线都必须缩短或分割;如果整个地区存在差距,那么将重叠的线路必须转移到补偿。
Layout manager为给定的行line提供一个矩形,然后要求Text Container调整矩形以适应。提出的矩形通常跨越Text Container的边界矩形,但它可以更窄或更宽,也可以部分或完全位于边界矩形之外。Layout manager发送Text Container以调整建议的矩形的消息是lineFragmentRectForProposedRect:atIndex:writingDirection:remainingRect :,它根据布局文本的方向返回可用于建议矩形的最大矩形。它还返回一个包含任何剩余空间的矩形,例如Text Container中孔或间隙另一侧的空间。
Layout manager在实际将文本放入矩形中时进行最后的调整。这种调整是由Text Container固定的一小部分,称为行片段填充,它将线段分段矩形的每个末端上的部分定义为空白。文本以这个数量嵌入在线段分段矩形内(矩形本身不受影响)。填充允许在边缘(和任何孔周围)对Text Container的区域进行小尺寸调整,并使文本不会直接与该区域附近显示的任何其他图形相邻。您可以使用lineFragmentPadding属性从其默认值更改填充。请注意,行碎片填充不是表达边距的合适方法。对于文档边距,您应该在其封闭视图中设置UITextView对象的位置和大小。对于文本边距,您应该设置文本视图的textContainerInset属性。另外,可以使用NSMutableParagraphStyle属性(如headIndent)为单个段落设置缩进值。
除了返回行分段矩形本身之外,Layout manager还会返回一个称为使用矩形的矩形。这是实际包含要绘制的字形或其他标记的线段分段矩形的一部分。按照惯例,两个矩形都包含行分段填充和行间空间(根据字体的行高度量度和段落的行间距参数计算)。但是,段落间距(之前和之后)以及文本周围添加的任何空格(如由中心间距文本引起的空间)仅包含在线段矩形中,并且不包含在使用的矩形中。
指定排除路径Specifying Exclusion Paths
文本容器维护一个UIBezierPath对象数组,表示接收器的边界矩形内的排除路径。当布局管理器发送文本容器时,lineFragmentRectForProposedRect:atIndex:writingDirection:remainingRect:message提出一个与排除路径定义的区域相交的线段矩形,文本容器返回一个不包括该区域的调整后的线段矩形。
指定多页和多列绘制Specifying Multipage and Multicolumn Layouts
简单的例子都是一个text storage,一个text layout manager,一个text container,一个textview,然后显示到屏幕上。在interface builder里拖入一个textview时,发生的就是这个场景。当使用多个text container,每个关联一个textview,更富仔的布局安排时必要的。比如,为了支持分页,一个app可以配置文字对象如图:
 
每个container关联文档的一页。显示文字的view是可以被嵌入到一个custom view 对象,比如潜入UIScrollView。
多列文档可以用类似的对象排列来建模,如图:
 
现在有两个文本容器,一个用于页面上的每一列,而不是一个文本容器对应一个页面。每个文本容器都控制着文档的一部分。在显示文本时,字形首先放置在左上方的容器中。当该视图中没有更多空间时,布局管理器通知其代表已完成填充容器。代表可以检查是否需要布置更多文本,并在需要时添加其他文本容器。布局管理器继续在下一个容器中布置文本,完成后通知委托,等等。同样,自定义视图(以蓝色矩形表示)为这些文本列提供了一个画布。
您不仅可以拥有多个文本容器,还可以让多个NSLayoutManager对象访问相同的文本存储。如下图,显示了具有多个布局管理器的对象布局。这种安排的效果是提供同一文本的多个视图。如果用户在顶视图中更改文本,则更改立即反映在底部视图中(假定更改的位置在底视图的边界内)。
 
底层文字处理技术 Lower Level Text-Handling Technologies
简单文字绘制
除了用于显示和编辑文本的UIKit类以外,iOS还包括几种直接在屏幕上绘制文本的方法。绘制简单字符串的最简单和最有效的方法是将UIKit添加到名为UIStringDrawing的类中的NSString类。这些扩展包括使用各种属性绘制字符串的方法,无论您希望在屏幕上显示哪些字符。在实际绘制字符串之前,还有计算渲染字符串大小的方法,这可以帮助您更精确地布局应用内容。
Important:使用UIKit框架的文本对象,而不是直接绘制,有一些很好的理由可以避免直接绘制文本。一个是性能。尽管UILabel对象也绘制其静态文本,但它只绘制一次,而文本绘制例程通常会重复调用。文本对象也提供了更多的交互。
UIStringDrawing类的方法在给定的点(对于单行文本)绘制文字,或者,在确定的矩形(多行文本)内绘制文字。你可以传入绘制使用的属性,比如,字体,换行模式,基线等。UIStringDrawing类的方法允许你精确的调整绘制文字的位置,和其他你View的内容混合。UIStringDrawing类的方法也允许你预先基于所需的字体和样式属性计算文字矩形的范围。
你也可以使用Core Animation的CATextLayer类来做简单的文字绘制工作。CATextLayer的对象存储一个空字符串或者 attributed string 作为他的内容,提供一组影响内容的属性集合,比如font,font size,textcolor和truncation behavior截断行为。CATextLayer(CALayer的子类)的好处是,他的properties属性是有动画属性的。Core Animation 和QuartzCore关联。因为实例化的CATextLayer知道如何在当前graphics context里绘制自己,当使用实例时,你不需要了解任何显式的绘制命令。(问题:那么和TextKit绘制的区别呢?是否有text storagetext layout managertext cotainer?)
Core Text
Core Text是一个定制文字布局和字体管理的技术。开发者一般不需要直接使用Core Text。Text kit是在Core Text基础上的上层封装,与core text有一样的优势,比如速度和智能排版。另外,text kit 提供了一个非常好的基础封装,而使用core text,你必须自己实现。
但是, 开发这可以直接操作Core Text API。给那些 具有自己的布局引擎的应用程序使用 – 例如,具有自己的页面布局引擎的文字处理器可以使用Core Text来生成字形并将它们相对放置。
Core Text 作为framework实现,公开的API和Core Foundation类似,基于过程的,不是基于对象的。当时类似于对象的不透明类型。该API与Core Foundation和Core Graphics都集成在一起。 例如,Core Text在许多输入和输出参数中使用Core Foundation和Core Graphics对象。 此外,由于许多Core Foundation对象都与Foundation框架中的对应方“toll-free bridge”,因此可以在Core Text函数的参数中使用一些Foundation对象。
注意:如果您使用Core TextCore Graphic来绘制文本,请记住您必须对当前文本矩阵CTM应用翻转变换以使文本以正确的方向显示也就是说,绘图原点位于文本的左上角字符串的边界框。
Core text有两部分组成:一个布局引擎和字体技术,每个部分背后是他们自己的不透明类型集合。
Core Text 布局不透明类型 Core Text Layout Opaque Types
Core Text需要两个不透明类型的对象:一个attributed string(CFAttributedStringRef)和一个graphic path(CGPathRef)。 attributed string对象封装了支持显示文本的字符串,并包含定义字符串中字符风格方面的属性 ,例如字体和颜色。 图形路径定义了文本框架的形状,相当于一个段落。
runtime的Core Text对象形成一个反映正在处理的文本级别的层次结构(见图)。这个层次结构的顶部是framesetter对象(CTFramesetterRef)。使用attributed string和graphic path作为输入,framesetter会生成一个或多个文本的frame(CTFrameRef)。由于文本排列在一个frame中,framesetter会为其应用段落样式,包括alignment, tab stops, line spacing, indentation, and line-breaking mode等属性。
为了生成frame,framesetter调用一个typesetter对象(CTTypesetterRef)。typesetter将attributed string中的字符转换为字形,并将这些字形填充到 填充文本 的 frame 的行中。 (字形是用于表示字符的图形形状。)框中的行由CTLine对象(CTLineRef)表示。一个CTFrame对象包含一个CTLine对象数组。
CTLine对象又包含一个graph runs数组,由CTRunRef类型的对象表示。graph run是一系列具有相同属性和方向的连续字形。虽然typesetter对象返回CTLine对象,但它会从graph run数组中组成这些行。
使用CTLine类型的方法,你可以从一个attributed string绘制一行文字,不必一定通过CTFramesetter对象。你只需将文本的原点放在文本基线上,并请求线对象绘制自己。
Core Text  字体不透明类型 Core Text Font Opaque Types
字体对核心文本中的文本处理至关重要。typesetter 对象使用字体(attributed string中的)来从字符转换字形,放置这些字形一个和另一个对应。graphics context 是core text字体的核心。你可以使用graphics context的方法来设置当前字体、绘制字形;或者你可以创建一个CTLine对象(从attributed string中),使用这个CTLine对象方法来绘制掉graphics context。Core Text 字体系统本地化处理unicode 字体。
字体系统包括三个不透明类型:CTFont, CTFontDescriptor, and CTFontCollection: 
  • 字体对象(CTFontRef)由一个point size和指定特征集(来自 一个 transformation matrix)初始化。你可以查询这个font对象的字符-字形印社、编码encoding,字形数据和指标比如ascent, leading等。Core Text 页提供一个自动的字体替代机制——font cascading。
  • Font descriptor object(CTFontDescriptorRef)经常用于创建字体对象。他允许你指定一个包含PostScript名称,font family,fontstyle和traits特性(如 bold,italic)的属性字典,而不是处理一个复杂的transformation matrix。
  • Font collection objects(CTFontCollectionRef)是font descriptors的组,提供诸如子弟遍历和访问全局/自定义font集合的服务。
调用CTFontCreateWithName方法可以转换UIFont对象到CTFont对象,传递UIFont封装的字体名称和point size 。
自定义文字输入
根据Text Kit和Core Text的定义,文字输入也可以自定义,可以根据需要,精确控制输入文字的方式。iOS的文本输入系统管理键盘。它将按键解释为在特定键盘中按特定键以适合某些语言。然后它将关联的字符发送到目标视图进行插入。正如简单文本输入中所解释的,视图类必须采用UIKeyInputprotocol协议来插入和删除插入符(插入点)处的字符。
但是,文本输入系统不仅仅是简单的文本输入。例如,它管理自动更正和多段输入,它们都基于当前的选择和上下文。汉字(日语)和汉字(汉语)等表意语言需要多级文本输入,这些输入是从语音键盘输入的。为了获得这些功能,自定义文本视图必须通过采用UITextInput协议并实现相关的客户端类和协议与文本输入系统进行通信。

留下评论

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.