在iOS中如何正確的實現行間距與行高

來源:互聯網
上載者:User

標籤:解決   差距   body   bash   hub   width   point   lib   virtual   

https://juejin.im/post/5abc54edf265da23826e0dc9

最近準備給 VirtualView-iOS 的文本元素新增一個 lineHeight 屬性,以便和 VirtualView-Android配合時能更精確的保證雙平台的一致性。面向 Google 以及 Stack Overflow 編程了一會後發現,能查到的資料大部分是介紹如何? lineSpacing 屬性,而不是 lineHeight。但是我就是因為 iOS 和 Android 的預設 lineSpacing 不一致所以才想實現個 lineHeight 啊!還是需要自己動手豐衣足食,順帶整理成文章造福後人。

關於行間距 lineSpacing

先貼出一張 iOS 中 UILabel 的預設排版樣式:

 

 

大家也都能看出來,預設的排版樣式中,文本的行間距很小,顯得文本十分擠。

這種時候,設計師就會提出行間距的需求,希望讓文本展示得更美觀。類似的標註就會像這樣:

 

 

通常來說既然設計師要求的是行間距,那麼我們直接設定 lineSpacing 就好。但是 UILabel 是沒有這麼一個直接暴露的屬性的,想要修改 lineSpacing,我們需要藉助 NSAttributedString 來實現,示意代碼:

NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];paragraphStyle.lineSpacing = 10;NSMutableDictionary *attributes = [NSMutableDictionary dictionary];[attributes setObject:paragraphStyle forKey:NSParagraphStyleAttributeName];label.attributedText = [[NSAttributedString alloc] initWithString:label.text attributes:attributes];

運行一下觀察效果:

 

 

雖然用我們的眼睛看上去好像沒什麼問題,但是設計師的火眼金睛一下就能看出來,和設計稿要求的有差距:

 

 

怎麼會成這樣!?這跟說好的不一樣對不對!?不要慌,我來細細解釋下。

正確的實現行間距

先看:

 

 

紅色地區是預設繪製單行文本會佔用的地區,可以看到文字的上下是有一些留白的(藍色和紅色重疊的部分)。設計師是想要藍色地區高度為 10pt,而我們直接設定 lineSpacing 會將兩行紅色地區中間的綠色地區高度設定為 10pt,這就是問題的根源了。

那麼這個紅色的地區高度是多少呢?答案是 label.font.lineHeight,它是使用指定字型繪製單行文本的原始行高。

知道了原因後問題就好解決了,我們需要在設定 lineSpacing 時,減去這個系統的內建邊距:

NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];paragraphStyle.lineSpacing = 10 - (label.font.lineHeight - label.font.pointSize);NSMutableDictionary *attributes = [NSMutableDictionary dictionary];[attributes setObject:paragraphStyle forKey:NSParagraphStyleAttributeName];label.attributedText = [[NSAttributedString alloc] initWithString:label.text attributes:attributes];

觀察一下效果,完美契合:

 

 

關於行高 lineHeight

如果你只關心 iOS 裝置上的文本展示效果,那麼看到這裡就已經夠了。但是我需要的是 iOS 和 Android 展現出一模一樣的效果,所以光有行間距是不能滿足需求的。主要的原因在前言也提到了,Android 裝置上的文字上下預設留白(上一節圖中藍色和紅色重疊的部分)和 iOS 裝置上的是不一致的:

 

 

左側是 iOS 裝置,右側 Android 裝置,可以看到同樣是顯示 20 號的字型,安卓的行高會偏高一些。在不同的 Android 裝置上使用的字型不一樣,可能還會出現更多的差別。如果不想辦法抹平這差別,就不能真正意義上實現雙端一致了。

這時候我們可以通過設定 lineHeight 來使得每一行文本的高度一致,lineHeight 設定為 30pt 的情況下,一行文本高度一定是 30pt,兩行文本高度一定是 60pt。雖然文字的渲染上會有細微的差別,但是布局上的差別將被完全的抹除。lineHeight 同樣可以藉助 NSAttributedString 來實現,示意代碼:

NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];paragraphStyle.maximumLineHeight = lineHeight;paragraphStyle.minimumLineHeight = lineHeight;NSMutableDictionary *attributes = [NSMutableDictionary dictionary];[attributes setObject:paragraphStyle forKey:NSParagraphStyleAttributeName];label.attributedText = [[NSAttributedString alloc] initWithString:label.text attributes:attributes];

運行一下觀察效果:

 

 

在 debug 模式下確認了下文本的高度的確正確的,但是為什麼文字都顯示在了行底呢?

修正行高增加後文字的位置

修本文字在行中展示的位置,我們可以用 baselineOffset 屬性來搞定。這個屬性十分有用,在實現上標下標之類的需求時也經常用到它。經過調試,發現最合適的值是 (lineHeight - label.font.lineHeight) / 4(尚未搞清楚為什麼是除以 4 而不是除以 2,希望知道的老司機指點一二)。最終的程式碼範例如下:

NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];paragraphStyle.maximumLineHeight = lineHeight;paragraphStyle.minimumLineHeight = lineHeight;NSMutableDictionary *attributes = [NSMutableDictionary dictionary];[attributes setObject:paragraphStyle forKey:NSParagraphStyleAttributeName];CGFloat baselineOffset = (lineHeight - label.font.lineHeight) / 4;[attributes setObject:@(baselineOffset) forKey:NSBaselineOffsetAttributeName];label.attributedText = [[NSAttributedString alloc] initWithString:label.text attributes:attributes];

貼一下在不同字型大小和行高下的展示效果:

 

 

行高和行間距同時使用時的一個問題

不得不說行高和行間距我們都已經可以完美的實現了,但是我在嘗試同時使用它們時,發現了 iOS 的一個 bug(當然也可能是一個 feature,畢竟不 crash 都不一定是 bug):

 

 

著色的地區都是文本的繪製地區,其中看上去是橙色的地區是 lineSpacing,綠色的地區是 lineHeight。但是為什麼單行的文本系統也要展示一個 lineSpacing 啊!?坑爹呢這是!?

好在我們通常是行高和行間距針對不同的需求分別獨立使用的,它們在分開使用時不會觸發這個問題。所以在 VirtualView-iOS 庫中,我暫且將高度計算的邏輯保持和系統一致了。

總結

至此,成功的為 VirtualView-iOS 添加了對 lineHeight 屬性的支援,更多的實現細節大家可以到開源庫中直接看原始碼。希望我們的 Tangram 方案可以更加完善,協助更多的人一次開發兩端同時使用,用一塊七巧板拼出大千世界。

在iOS中如何正確的實現行間距與行高

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.