iOS 如何精確還原 UI 稿多行文字間距

關於 iOS 中多行文字行間距這個問題蛋疼了幾年了,回憶一下整個歷程:git

一開始,UI 同窗使用 PhotoShop 實現 UI 稿,PhotoShop 的 Label 在相同字體下的高度與 iOS 比就不許,而且使用標註工具進行文字標註時老是緊貼着字形的上下邊進行標註,而字體自己有 LineHeight,字形上下是有間距的。爲了達到 UI 稿效果,只能用模擬器對着相同尺寸 UI 稿,用標尺工具一點點比較,試出間距值,標註值僅供參考。github

後來 UI 同窗換成了用 Sketch 實現 UI 稿,因爲 Sketch 使用和 iOS 相同的文本渲染技術,在 Sketch 上新建一個 Label,文本帶 LineHeight,有間距,單行文字或文字與其餘元素之間的間距終於準確了。工具

可是 Sketch 中處理多行文本時只有 LineHeight 的概念,沒有 UILabel 中 LineSpacing 的概念,LineSpacing 只會在行與行中間添加間距,每一行的 LineHeight 保持不變,致使 UI 稿中多行文字修改 LineHeight 以後,用 LineSpacing 並不能完美匹配 UI 稿效果,並且 LineHeight 的變化也會致使文本在和其餘控件對齊時與標註對不上。NSParagraphyStyle 雖然有 maximumLineHeight 和 minimumLineHeight 屬性,但設置之後是在文本頂部多出間距,而不是上下均勻間距。爲了解決這個問題,參考過 iOS 文本對齊,如何像素般精確還原設計稿,使用 Sketch 插件將 LineHeight 修正成 LineSpacing 的效果,但 UI 同窗反饋插件不能用,我也沒仔細研究如何定製 Sketch 插件,另外,每次用插件修正也比較麻煩,UI 同窗存在遺漏的可能性。post

另外,iOS 的 LineSpacing 一直有個 Bug,一旦中文設置了 LineSpacing,在單行狀況下底部會多出 LineSpacing 的間距,多行時就沒有這個問題,英文單行也沒有這個問題。爲了解決這個問題,會判斷文字是否超過了一行,若是不超過一行就不設置 LineSpacing。後來嫌麻煩,直接用 baseline 對齊而不是 bottom 對齊,offset 須要加上字體 descent 的大小。字體

今天偶然看到了 在iOS中如何正確的實現行間距與行高 - 掘金 這篇文章,豁然開朗。雖然設置 maximumLineHeight 和 minimumLineHeight 會致使顯示有偏移,但總體高度是對的,利用 baselineOffset 將偏移修復便可,修復公式爲 (lineHeight - label.font.lineHeight) / 4ui

NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
paragraphStyle.maximumLineHeight = lineHeight;
paragraphStyle.minimumLineHeight = lineHeight;
NSMutableDictionary *attributes = [NSMutableDictionary dictionary];
attributes[NSParagraphStyleAttributeName] = paragraphStyle;
CGFloat baselineOffset = (lineHeight - font.lineHeight) / 4;
attributes[NSBaselineOffsetAttributeName] = @(baselineOffset);
複製代碼

通過同 Sketch 對比,與 UI 效果一致。因爲設置的是 LineHeight,中文單行文字也沒有了底部多出間隔的問題了。最後將相關代碼抽成一個 Utils,之後若是 UI 修改了文字的 LineHeight,直接使用這個 Utils 配置 NSAttributedString,就能完美適配 UI 的效果和標註,神清氣爽!spa

一些注意事項:插件

  1. 每種字體的 LineHeight 是不一樣的,例如 SFUI 的 LineHeight 是字號的 1.2 倍,PingFangSC 的 LineHeight 是字號的 1.4 倍。
  2. SFUI 中沒有中文字體,最後系統會 fall back 到 PingFangSC,字形的顯示是相同的,可是因爲字體不用,致使 LineHeight 不同。用 systemFontOfSize:sizefontWithName:@"PingFangSC-Regular" size:size 設置 UILabel 的 font,相同中文內容的 UILabel 高度不同。
  3. baselineOffset 很奇怪,移動的效果是設置值的兩倍,例如設置 1 pt,向上移動 2 pt,因此修復公式最後是 / 4 而不是 / 2。

Article by 傳人 Joe設計

相關文章
相關標籤/搜索