iOS 文書處理相關

hi_xgb發表於2016-11-22

在人類的歷史發展中,人們有溝通交流的需求,於是出現了語言和文字,之後又發明了造紙術和印刷術,使得人們的想法可以儲存記錄並傳播開來。

在 iOS 開發中,文書處理可以說是最基礎常見的一部分內容,系統控制元件例如 UILabel、UITextField、UITextView 等幫我們做了很多工作讓我們可以很方便地展示一段文字。但是當我們要做更深入的定製展示時,系統提供的這些控制元件就無法滿足需求了,這時候就要深入去了解一下系統是如何處理文字展示的。

以下內容是我在探究 iOS 系統對文書處理時做的一些記錄,主要是一些概念性的內容,讓大家對 iOS 文書處理有一個大概瞭解,涉及到具體需求時知道查詢哪方便的資料,包括了 Unicode、UIFont、TextKit、CoreText、Unicode雙向演算法等。

Nsstring 和 Unicode

(一)歷史

計算機沒法直接處理文字,它只和數字打交道。為了在計算機裡用數字表示文字,指定了一個從字元到數字的對映。這個對映就叫做編碼(encoding)。最開始的對映是 ASCII 編碼,但是能表示的字元有限,因此後來用 Unicode 統一編碼。

(二)Unicode概要

Unicode 可以看做是對各編碼系統的統一,但不通用,有一些很古老的編碼系統無法相容。

(三)Unicode特性
  1. Unicode 以抽象的方式代表一個字元,而不規定這個字元如何渲染(render)。
  2. 組合字元序列,有些字元可以由單一碼點或由多個碼點組成,雖然外觀和意義相同,在 Unicode 語境下並不相等,但符合 canonically equivalent
(四)Unicode格式轉換

UTF(Unicode Transformation Formats)

(五)NSString

關於 NSString,最需要記住的是:NSString 代表的是用 UTF-16 編碼的文字,長度、索引和範圍都基於 UTF-16 的碼元。對於這一點要是不注意會有以下一些陷阱:

  • 長度

我們經常用 NSString 的 length 方法來獲取一個字串的長度,在大多數情況下這個方法都沒有問題,但是當一個字串中包含 emoji 時,這個返回的長度值並不準確,以下是具體例子:

以下程式碼可以獲取實際的長度

  • 隨機訪問

用 characterAtIndex: 方法以索引方式直接訪問 unichar 會有同樣的問題。可以用 rangeOfComposedCharacterSequenceAtIndex: 來確定特定位置的 unichar 是不是代表單個字元(可能由多個碼點組成)的碼元序列的一部分。每當給另一個方法傳入一個內容未知的字串的範圍作引數時都應該這樣做,確保 Unicode 字元不會被從中間分開。

  • 遍歷

使用 rangeOfComposedCharacterSequenceAtIndex: 的時候,可以寫一個程式碼套路來正確地迴圈字串裡所有的字元,但每次要遍歷一個字串時都得這樣做太不方便了。幸運的是,NSString 有更好地方式:enumerateSubstringsInRange:options:usingBlock: 方法。這個方法把 Unicode 抽象的地方隱藏了,能讓你輕鬆地迴圈字串裡的組合字串、單詞、行、句子或段落。你甚至可以加上 NSStringEnumerationLocalized 這個選項,這樣可以在確定詞語間和句子間的邊界時把使用者所在的區域考慮進去。要遍歷單個字元,把引數指定為 NSStringEnumerationByComposedCharacterSequences:

  • 比較

有些字元可以由單一碼點或由多個碼點組成,雖然外觀和意義相同,在 Unicode 語境下並不相等。isEqual: 和 isEqualToString: 這兩個方法都是一個位元組一個位元組地比較的。如果希望字串的合成和分解的形式相吻合,得先自己正規化:

上面這些內容可以點選這裡瞭解更多。

TextKit

下圖列出了 iOS 系統下和文書處理相關的元件,其中 TextKit 是從 iOS7 開始引入的,旨在幫助開發者實現更多的文字定製。

11128529-6b62d5732fbcf329

Text Kit is a set of classes and protocols that provide high-quality typographical services which enable apps to store, lay out, and display text with all the characteristics of fine typesetting, such as kerning, ligatures, line breaking, and justification.

以上引用了一段官網對 Text Kit 的介紹,翻譯過來就是:
Text Kit 是一系列類和協議,這些類和協議提供了高效能的排版服務,這個服務可以讓應用以很好的排版形式儲存、佈局和展示所有的字元,比如字間距、連筆、斷行、兩端對齊。

Text Kit 中有幾個關鍵的元件,如下圖所示,

12128529-b31b76b6bfcc378f

layoutManager 將 textStorage 儲存的內容根據 textContainers 定義的區域佈局到 textViews(UITextView)裡。

在 MVC 中,textStorage 和 textContainers 相當於 M,textViews 相當於 V,leyoutManager 相當於 C。

An NSLayoutManager object orchestrates the operation of the other text handling objects. It intercedes in operations that convert the data in an NSTextStorage object to rendered text in a view’s display area. It maps Unicode character codes to glyphs and oversees the layout of the glyphs within the areas defined by NSTextContainer objects.

NSLayoutManager 將 Unicode 字元轉換成 glyphs(字形),並在 NSTextContainer 定義的範圍內佈局這些字形。

The layout manager performs the following actions:

  • Controls text storage and text container objects
  • Generates glyphs from characters
  • Computes glyph locations and stores the information
  • Manages ranges of glyphs and characters
  • Draws glyphs in text views when requested by the view
  • Computes bounding box rectangles for lines of text
  • Controls hyphenation
  • Manipulates character attributes and glyph properties

layout manager 會做如下一系列操作:

  • 控制 text storage 和 text container
  • Unicode 字元轉換成 glyphs(字形)
  • 計算字形的位置資訊並儲存起來
  • 管理字元的範圍資訊
  • 將字形繪製到檢視上
  • 計算每一行的矩形包裹資訊
  • 處理斷字
  • 處理文字的屬性,例如字型、顏色、下標

Text Kit handles three kinds of text attributes:

  1. character attributes, paragraph attributes, and document attributes.
  2. Character attributes include traits such as font, color, and subscript, which can be associated with an individual character or a range of characters.
  3. Paragraph attributes are traits such as indentation, tabs, and line spacing. Document attributes include documentwide traits such as paper size, margins, and view zoom percentage.
  1. Character attributes:字型、顏色、下標
  2. Paragraph attributes:縮排、製表符、行距
  3. Document attributes:頁數、頁間距、頁縮放比例

下面列出了一些 Text Kit 的常見用法,

13128529-65c0de5c46187b7c
14128529-38a25e93ea1803a4
 15128529-7d16b3dbc4b59a7e
 16128529-eed1fd86b410c11e

UIFont

我們可以通過設定不同的字型來改變字元渲染到頁面上的樣式,UIFont 裡有一些度量資訊(metrics),如下圖所示,

17128529-32efbe7edd490cd4

這些資訊在 UIFont 裡都能獲取到,以下是它們的對應關係

18128529-aab9fe4b423a6f3f

UIFont 的 metrics 有一個具體應用,比如我們想讓一個區域最多顯示6行的文字,如果使用 UILabel,我們可以指定 numberOfLines 屬性,在不使用 UILabel 的情況下,我們就可以用到 UIFont 的 lineHeight 屬性了,用法如下:

CoreText

19128529-a118845a78daa848

CTFramesetter生成CTFrame,每一個CTFrame代表一個段落。一個CTFrame可以只是一行很長的CTLine或者包含多個CTLine,一個CTLine代表一行文字。

Unicode雙向演算法

雙向文字是指同時包含了兩種書寫方向的文字,也就是從左到右和從右到左的文字同時存在,預設根據第一個字元的 Unicode 屬性定義全域性方向。

書寫方向與文字相關,與語言的關係不大。一種語言可能有多種文字,使用英語時從左到右書寫,使用阿拉伯語時使用從右到左書寫。

20128529-9f67f53127d0f237

這些被加入文字中的 Unicode 控制字元在顯示介面上是不可見的,也不佔用任何顯示空間。它們只是在默默地影響著雙向文字的顯示。

Unicode 控制字元又可以分為兩類,

第一類為隱性雙向控制字元:

  • U+200E: LEFT-TO-RIGHT MARK (LRM)
  • U+200F: RIGHT-TO-LEFT MARK (RLM

簡單來說,您可以將這類的控制字元看成是不會顯示出來的強字元,LRM 為從左到右的強字元,而 RLM 為從右到左的強字元。

而第二類當然就是顯性雙向控制字元:

  • U+202A: LEFT-TO-RIGHT EMBEDDING (LRE)
  • U+202B: RIGHT-TO-LEFT EMBEDDING (RLE)
  • U+202D: LEFT-TO-RIGHT OVERRIDE (LRO)
  • U+202E: RIGHT-TO-LEFT OVERRIDE (RLO)
  • U+202C: POP DIRECTIONAL FORMATTING (PDF)

這類控制字元需要成對使用,列表中的前四個為開始字元,而最後一個為結束字元。當雙向演算法遇到 LRE 時,接下來文字片段內的方向開始變為從左到右。當雙向演算法遇到 RLE 時,接下來文字片段內的方向開始變為從右到左。當遇到 LRO 時,雙向演算法會將後面所有文字的雙向屬性視為從左到右強字元。當遇到 RLO 時,雙向演算法會將後面所有文字的雙向屬性視為從右到左強字元。如果一旦遇到 PDF 字元,雙向屬性的狀態就會恢復到最後一個 LRE、RLE、LRO 或 RLO 之前的狀態。

更多資料參考

http://www.ibm.com/developerworks/cn/web/1404_xiayin_bidihtml/
http://www.iamcal.com/understanding-bidirectional-text/

總結

以上總體描述了 iOS 系統處理文字時用到的知識點,每一部分都沒有深入說明,只是讓大家有個整體概念,在每一部分的後面都貼上了我在查詢資料時覺得比較好的參考資料,可以再深入瞭解一下。

轉載請註明出處,有任何疑問都可聯絡我,歡迎探討。
微訊號:xieguobihaha

打賞支援我寫出更多好文章,謝謝!

打賞作者

打賞支援我寫出更多好文章,謝謝!

任選一種支付方式

iOS 文書處理相關 iOS 文書處理相關

相關文章