objc.io 第9期之字符串渲染

本文轉自:http://iosinit.com/?p=880

注:本文翻譯自 objc.io,原文鏈接是:String Rendering

在本期中我們已經討論了很多關於字符串不同的話題,從編碼到本地化再到語法分析。但多數情況下,字符串最終還是需要被繪製到屏幕上供用戶查看、交互。這篇文章涵蓋了最基本、最好的練習,以及在用戶界面上呈現字符串可能遇到的常見陷阱。

如何將字符串繪製到屏幕上

爲了簡單起見,我們先看看 UIKit 在字符串渲染方面爲我們提供了哪些控件。之後我們將討論一下對於字符串的渲染, iOS 和 OS X 系統中有哪些相似和不同。

UIKit 提供了很多可以在屏幕上顯示和編輯文本的類。每一個類都是爲特定使用情況準備的,所以爲了避免不必要的問題,爲你手上的任務挑選正確的工具是非常重要的。

UILabel

UILabel 是將文本繪製到屏幕上最簡單的方式。它是 UIView 的一個子類,用來顯示少量的只讀文本。文本可以被展示在一行或多行,如果文本不能適應指定的空間我們還可以使用不同的方式裁剪。儘管labels 使用的方式很簡單,但是這裏有幾個技巧還是值得我們提一提的。

labels 默認只顯示一行,但是你可以將 numberOfLines 屬性設爲其他值來改變這一行爲。將它設置爲一個大於 1 的值,文本的行數將會被限制爲這個指定的值,如果設置爲0 ,則是告訴 label 不管文本佔多少行都顯示出來。

通過設置 text 屬性, Labels 可以顯示簡單的純文本,而設置 attributedText 屬性則可以讓label 顯示富文本。當使用純文本的時候,你可以使用 label 的 font , textColor ,textAlignment, shadowColor 和 shadowOffset 屬性改變它的外觀,如果你希望改變整個程序所有 Label 的風格,你也可以使用 [UILabel appearance] 這個方法來進行全局的更改。
Attributed strings 提供了更加靈活的風格可供選擇,字符串的不同部分可以使用不同的風格。讓我們看看常見佈局部分,下面給出 attributed strings 一些示例。(下文“常見佈局”那一節給出了具體的關於 Attributed String 的一些例子。)

除了通過上文提到的那些屬性來調整 UILabel 的顯示風格外,你還可以通過設置 UILabel 的這3個BOOL 值的屬性 adjustsFontSizeToWidth , minimumScaleFactor ,adjustsLetterSpacingToFitWidth 來讓 UILabel 根據所顯示的文本的內容自動地進行調整。如果你非常在意用戶界面的美觀,那麼你就不要開啓這些屬性,因爲這會使文字的顯示效果變得不那麼美觀,但是有的時候,比如在進行App的不同語言的本土化的時候,你會遇到一些很棘手的問題,除了使用這些選項外很難找到別的解決辦法。不信的話,你可以打開 iPhone,在設置中把系統語言改爲德語,然後你就會發現蘋果官方出品的程序裏到處都是被壓扁變了形的醜陋不堪的文本。這種處理方法並不完美,但有時卻很有用。

如果你使用這些選項讓 UIKit 壓縮你的文本以適配,如果壓縮的時候想讓文本保持在同一條基線上或需要對齊到左上角,那麼你可以定義 baselineAdjustment 屬性。然而,這個選項只對單行 labels起作用。

當你使用上述的方法讓文本自動縮放大小以適配你的 UILabel 時,你可以使用baselineAdjustment 這個屬性來調整縮放時文本的基準線,是保持統一基準線還是對齊到你的Label 的左上角。注意,這個屬性僅在單行的 Lable (即 numberOfLines 屬性值爲1時)中生效。

UITextField

像 labels 一樣, text fields 可以處理純文本或帶屬性的文本。但 labels 只是能顯示文本而已, text fields 還可以處理用戶輸入。然而, text fields 只限於單行文本。因此,UITextField 是 UIControl 的一個子類,它會掛鉤到 (hook into) 響應鏈,並且當用戶開始或結束編輯時分發 (deliver) 這些行爲消息。如果想要得到更多的控制權,你可以實現 text field的代理。

Text fields 有一系列控制文本輸入行爲的選項。 UITextField 實現了 UITextInputTraits 協議,這個協議需要你指定鍵盤外觀和操作的各種細節,比如,需要顯示哪種鍵盤,返回按鈕的響應事件是什麼。

當沒有文本輸入的時候 Text fields 還可以顯示一個佔位符,在右手邊顯示一個標準的清除按鈕,控制任意左右兩個輔助視圖。你還可以爲其設置一個背景圖片,這樣我們就可以用一個可變大小的圖片爲text field 自定義邊框風格了。

但每當你需要輸入多行文本的時候,你就需要使用到 UITextField 的大哥了……

UITextView

Text views 是顯示或編輯大量文本的理想選擇。 UITextView 是 UIScrollView 的一個子類,所以它能允許用戶前後滾動達到處理溢出文本的目的。和 text fields 一樣, text views 也能處理純文本和帶屬性的文本。 Text views 還也實現了 UITextInputTraits 協議來控制鍵盤的行爲和外觀。

但除了 text view 處理多行文本的能力外,它最大的賣點就是你可以使用、定製整個 Text Kit堆。你可以自定義行爲或爲 layout manager 、 text container 或  text storage 替換你自定義的子類。objc.io 第5期中有關於 Text Kit 方面的文章

不幸的是, UITextView 在 iOS 7 中還有些問題。目前還是1.0版本。它是基於OS X Text Kit 從頭開始重新實現的。iOS 7之前,它是基於 Webkit 並且功能很少。

Mac中又是什麼情況呢?

現在我們的討論已經覆蓋了 UIKit 中基本的 text 類,我們繼續解釋一下這些類在 AppKit 中結構的不同之處。

首先, AppKit 中並沒有類似 UILabel 的控件。而顯示文本最基本的類是 NSTextField 。我們將text field 設爲不可編輯、不可選擇,這樣便等同於iOS中的 UILabel 了。雖然 NSTextField聽起來類似於 UITextField ,但 NSTextField 並不限制於單行文本。

NSTextView,換句話說,就是等同於 UITextView ,它也爲我們揭露了整個 Cocoa TextSystem 。但它還囊括了很多額外的功能。很大的原因是因爲Mac是一個具有指針設備(鼠標)的電腦。最值得注意的就是包含了設置、編輯製表符的標尺。

Core Text

上面我們討論的所有類最終都使用 Core Text 佈局、繪製真實的符號。 Core Text 是一個非常強大的 framework ,它已經超出我們這篇文章討論的範圍。但是如果你曾經需要通過完全自定義的方式繪製文本(e.g.貝塞爾曲線),那你需要詳細的瞭解一下。

Core Text 在任何繪圖方面爲你提供了充分的靈活性。然而, Core Text 非常難於操作。它是一個複雜的 Core Foundation / C API 。 Core Text 在排版方面給了你充分的使用權。

在 Table View 中顯示動態 Text可能和所有人都打過交道的字符串繪製方法就是最常見的可變高度的 table view cells 。你能在社交媒體應用中見到這種。 table view 的 delegate 有一個方法。 tableView:heightForRowAtIndexPath: ,這便是用來計算高度的。iOS 7之前,很難通過一種可靠的方式使用它。

在我們的示例中,我們將會在 table view 中顯示一列語錄:


首先,爲了實現完全的自定義,我們創建一個 UITableViewCell 的子類。在這個子類中,我們需要親自爲我們的 label 佈局:

MyTableViewCellInset 被定義爲一個常量,所以我們可以將它用在 table view 的 delegate的高度計算中。最簡單、準確計算高度的方法是將字符串轉換成帶屬性的字符串,然後計算出帶屬性字符串的高度。我們使用 table view 的寬度減去兩倍的 MyTableViewCellInset 常量(前面和後面的空間)。爲了計算真實的高度,我們使用 boundingRectWithSize:options:context: 這個方法。

第一個參數是限制 text 大小的。我們只需要關心寬度的限制,因此我們爲高度傳一個最大值常量CGFLOAT_MAX 。第二個參數是非常重要的:如果你傳一個其他值, bounding rect 無疑會出錯。如果你想要調整字體縮放或進行追蹤,你可以使用第三個參數。最終,一旦我們得到 boundingRect ,我們需要再次加上 inset

對於 bounding rect 的結果還有兩件敏感的事情,除非你讀了文檔,不然這兩件事你不一定會知道:返回的 size 返回一個小數,文檔中讓我們使用 ceil 將結果四捨五入。最終,結果可能是會比實際的大一點。

請注意,因爲我們的 text 是純文本時,我們創建的 attributedBodyTextAtIndexPath: 方法也會在 tableView:cellForRowAtIndexPath: 中用到。這樣,我們需要確保他們保持同步。

還有,通過閱讀文檔(如下截圖),我們發現 iOS 7 發佈後,很多方法都被棄用了。如果你通過查找網頁或 StackOverflow,你會發現很多測量字符高度的變通方法。因爲蘋果對文本框架進行了重大檢修(在內部實現中,所有的東西都使用 TextKit 進行繪製了,而不是 WebKit ),所以請使用新方法。


另一個動態調整 table view cell 大小的選擇就是使用 Auto Layout ,你可以在這篇博文中找到更詳細的說明。然後你可以利用 contained lables 的 intrinsicContentSize。然而,現在自動佈局比手動計算要慢很多。可是對於原型開發,這很完美:它允許你快速調整 constraints 並且移動事物(特別當你 cell 中不止一個控件時這顯得特別重要)。一旦你完成產品的設計迭代,然後你就可以用手動佈局的方式重新編寫代碼。

使用 Text Kit 和 NSAttributedString 進行佈局

使用 Text Kit,你將會擁有令人驚訝的靈活性來創建專業級別的文本佈局。隨着這些靈活性帶來的是如何組合爲數衆多的選項來完成複雜的佈局。

我們準備給出幾個示例並強調一些常見的佈局問題,同時給出解決方案。

經典的文本

首先,讓我們看一些經典的文本。我們將會使用 Jacomy-Régnier 的 Histoire des nombres et de la numération mécanique ,並設爲 Bodoni 字體。最終截屏效果如下所示:


這些都是由 Text Kit 完成的。兩段文字之間的裝飾也是文本,使用的是 Bodoni Ornaments 字體。

我們爲文體風格使用調整好的 text。第一段從最左邊開始,接下來的段落都會插入空格.

這有三種不同的風格:文體 風格,首行縮進的變化文體風格,裝飾物風格。

讓我們先設置 body1stAttributes

我們將字體設置爲 BodoniSvtyTwoITCTT 。這是字體的 PostScript 名。如果想尋找字體名,我們可以使用 +[UIFont familyNames] 首先得到可用的字體系列集合。一個字體系列就是我們所熟知的字型。每個字型或字體系列有一個或多個字體。爲了得到這些字體的名字,我們可以使用 +[UIFontfontNamesForFamilyName:] 。注意一下,當你處理多樣字體時, UIFontDescriptor 類非常有用,e.g.當你想要知道一個給定的字體是什麼版本的斜體。

許多設置位於 NSParagraphStyle 。我們創建一個默認風格的可變拷貝並做些調整。在我們的例子中,我們將會爲字體大小加上3pt。

接着,我們會爲這些段落的屬性創建一個拷貝並修改他們來創建 boddyAttributes ,(注意,這是我們段落的屬性,跟上文的 body1stParagraph 已經不是同一個了)

我們簡單的創建了一個屬性字典的可變拷貝,同時爲了改變段落風格我們也需要創建一個可變拷貝。將firstLineHeadIndent 設爲和字體大小一樣,我們便會得到想要的空格縮進。

接着,裝飾段落風格:

這個很容易理解。我們使用裝飾字體並將文本居中對齊。此外,在裝飾字符的前後我們都要加空白段落。

數據表格

接下來是顯示數字的 table 。我們想要將分數的小數點對齊顯示,i.e.英語中的”.”:


爲了達到這個目的,我們需要指定 table 將中心停在分隔符上。

對於上面這個示例,我們簡單的做一下:

列表

另一個常見的使用情況就像 list 這樣:


縮進相對容易設置。我們需要確保序列號(1)和 text 或者着重號和 text 之間有一個製表符。然後我們像這樣調整段落的風格:

我們將 headIndent 設置爲真實文本的縮進,將 firstLineHeadIndent 設置爲我們希望着重號具有的縮進。最終,和 headIndent 一樣,我們需要在相同的位置增加一個製表符。着重號後的製表符會確保這行文本從正確的位置開始繪製。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章