最近準(zhǔn)備給 VirtualView-iOS 的文本元素新增一個(gè) lineHeight 屬性协怒,以便和 VirtualView-Android 配合時(shí)能更精確的保證雙平臺(tái)的一致性揪惦。面向 Google 以及 Stack Overflow 編程了一會(huì)后發(fā)現(xiàn),能查到的資料大部分是介紹如何實(shí)現(xiàn) lineSpacing 屬性疗认,而不是 lineHeight。但是我就是因?yàn)?iOS 和 Android 的默認(rèn) lineSpacing 不一致所以才想實(shí)現(xiàn)個(gè) lineHeight 胺啤横漏!還是需要自己動(dòng)手豐衣足食,順帶整理成文章造福后人熟掂。
關(guān)于行間距 lineSpacing
先貼出一張 iOS 中 UILabel 的默認(rèn)排版樣式:
大家也都能看出來缎浇,默認(rèn)的排版樣式中,文本的行間距很小赴肚,顯得文本十分?jǐn)D素跺。
這種時(shí)候,設(shè)計(jì)師就會(huì)提出行間距的需求誉券,希望讓文本展示得更美觀指厌。類似的標(biāo)注就會(huì)像這樣:
通常來說既然設(shè)計(jì)師要求的是行間距,那么我們直接設(shè)置 lineSpacing 就好踊跟。但是 UILabel 是沒有這么一個(gè)直接暴露的屬性的踩验,想要修改 lineSpacing,我們需要借助 NSAttributedString 來實(shí)現(xiàn),示意代碼:
運(yùn)行一下觀察效果:
雖然用我們的眼睛看上去好像沒什么問題箕憾,但是設(shè)計(jì)師的火眼金睛一下就能看出來牡借,和設(shè)計(jì)稿要求的有差距:
怎么會(huì)成這樣!袭异?這跟說好的不一樣對(duì)不對(duì)D屏?不要慌扁远,我來細(xì)細(xì)解釋下俊鱼。
正確的實(shí)現(xiàn)行間距
先看示意圖:
紅色區(qū)域是默認(rèn)繪制單行文本會(huì)占用的區(qū)域,可以看到文字的上下是有一些留白的(藍(lán)色和紅色重疊的部分)畅买。設(shè)計(jì)師是想要藍(lán)色區(qū)域高度為 10pt并闲,而我們直接設(shè)置 lineSpacing 會(huì)將兩行紅色區(qū)域中間的綠色區(qū)域高度設(shè)置為 10pt,這就是問題的根源了谷羞。
那么這個(gè)紅色的區(qū)域高度是多少呢帝火?答案是 label.font.lineHeight
,它是使用指定字體繪制單行文本的原始行高湃缎。
知道了原因后問題就好解決了犀填,我們需要在設(shè)置 lineSpacing 時(shí),減去這個(gè)系統(tǒng)的自帶邊距:
觀察一下效果嗓违,完美契合:
關(guān)于行高 lineHeight
如果你只關(guān)心 iOS 設(shè)備上的文本展示效果九巡,那么看到這里就已經(jīng)夠了。但是我需要的是 iOS 和 Android 展現(xiàn)出一模一樣的效果蹂季,所以光有行間距是不能滿足需求的冕广。主要的原因在前言也提到了,Android 設(shè)備上的文字上下默認(rèn)留白(上一節(jié)圖中藍(lán)色和紅色重疊的部分)和 iOS 設(shè)備上的是不一致的:
左側(cè)是 iOS 設(shè)備偿洁,右側(cè) Android 設(shè)備撒汉,可以看到同樣是顯示 20 號(hào)的字體,安卓的行高會(huì)偏高一些涕滋。在不同的 Android 設(shè)備上使用的字體不一樣睬辐,可能還會(huì)出現(xiàn)更多的差別碰缔。如果不想辦法抹平這差別需频,就不能真正意義上實(shí)現(xiàn)雙端一致了敬拓。
這時(shí)候我們可以通過設(shè)置 lineHeight 來使得每一行文本的高度一致设哗,lineHeight 設(shè)置為 30pt 的情況下,一行文本高度一定是 30pt茫因,兩行文本高度一定是 60pt癞埠。雖然文字的渲染上會(huì)有細(xì)微的差別芽世,但是布局上的差別將被完全的抹除黔酥。lineHeight 同樣可以借助 NSAttributedString 來實(shí)現(xiàn),示意代碼:
運(yùn)行一下觀察效果:
在 debug 模式下確認(rèn)了下文本的高度的確正確的,但是為什么文字都顯示在了行底呢跪者?
修正行高增加后文字的位置
修正文字在行中展示的位置棵帽,我們可以用 baselineOffset 屬性來搞定。這個(gè)屬性十分有用渣玲,在實(shí)現(xiàn)上標(biāo)下標(biāo)之類的需求時(shí)也經(jīng)常用到它逗概。經(jīng)過調(diào)試,發(fā)現(xiàn)最合適的值是 (lineHeight - label.font.lineHeight) / 4
(尚未搞清楚為什么是除以 4 而不是除以 2忘衍,希望知道的老司機(jī)指點(diǎn)一二)逾苫。最終的代碼示例如下:
貼一下在不同字號(hào)和行高下的展示效果:
行高和行間距同時(shí)使用時(shí)的一個(gè)問題
不得不說行高和行間距我們都已經(jīng)可以完美的實(shí)現(xiàn)了,但是我在嘗試同時(shí)使用它們時(shí)枚钓,發(fā)現(xiàn)了 iOS 的一個(gè) bug(當(dāng)然也可能是一個(gè) feature铅搓,畢竟不 crash 都不一定是 bug):
著色的區(qū)域都是文本的繪制區(qū)域,其中看上去是橙色的區(qū)域是 lineSpacing搀捷,綠色的區(qū)域是 lineHeight星掰。但是為什么單行的文本系統(tǒng)也要展示一個(gè) lineSpacing 啊D壑邸氢烘?坑爹呢這是!家厌?
好在我們通常是行高和行間距針對(duì)不同的需求分別獨(dú)立使用的播玖,它們?cè)诜珠_使用時(shí)不會(huì)觸發(fā)這個(gè)問題。所以在 VirtualView-iOS 庫中饭于,我暫且將高度計(jì)算的邏輯保持和系統(tǒng)一致了蜀踏。
總結(jié):
至此,成功的為 VirtualView-iOS 添加了對(duì) lineHeight 屬性的支持镰绎,更多的實(shí)現(xiàn)細(xì)節(jié)大家可以到開源庫中直接看源代碼脓斩。希望我們的 Tangram 方案可以更加完善,幫助更多的人一次開發(fā)兩端同時(shí)使用畴栖,用一塊七巧板拼出大千世界随静。
補(bǔ)充:
NSFontAttributeName 設(shè)置字體屬性,默認(rèn)值:字體:Helvetica(Neue) 字號(hào):12
NSForegroundColorAttributeNam 設(shè)置字體顏色吗讶,取值為 UIColor對(duì)象燎猛,默認(rèn)值為黑色
NSBackgroundColorAttributeName 設(shè)置字體所在區(qū)域背景顏色,取值為 UIColor對(duì)象照皆,默認(rèn)值為nil, 透明色
NSLigatureAttributeName 設(shè)置連體屬性重绷,取值為NSNumber 對(duì)象(整數(shù)),0 表示沒有連體字符膜毁,1 表示使用默認(rèn)的連體字符
NSKernAttributeName 設(shè)定字符間距昭卓,取值為 NSNumber 對(duì)象(整數(shù))愤钾,正值間距加寬,負(fù)值間距變窄
NSStrikethroughStyleAttributeName 設(shè)置刪除線候醒,取值為 NSNumber 對(duì)象(整數(shù))
NSStrikethroughColorAttributeName 設(shè)置刪除線顏色能颁,取值為 UIColor 對(duì)象,默認(rèn)值為黑色
NSUnderlineStyleAttributeName 設(shè)置下劃線倒淫,取值為 NSNumber 對(duì)象(整數(shù))伙菊,枚舉常量 NSUnderlineStyle中的值,與刪除線類似
NSUnderlineColorAttributeName 設(shè)置下劃線顏色敌土,取值為 UIColor 對(duì)象镜硕,默認(rèn)值為黑色
NSStrokeWidthAttributeName 設(shè)置筆畫寬度,取值為 NSNumber 對(duì)象(整數(shù))返干,負(fù)值填充效果兴枯,正值中空效果
NSStrokeColorAttributeName 填充部分顏色,不是字體顏色犬金,取值為 UIColor 對(duì)象
NSShadowAttributeName 設(shè)置陰影屬性念恍,取值為 NSShadow 對(duì)象
NSTextEffectAttributeName 設(shè)置文本特殊效果,取值為 NSString 對(duì)象晚顷,目前只有圖版印刷效果可用:
NSBaselineOffsetAttributeName 設(shè)置基線偏移值峰伙,取值為 NSNumber (float),正值上偏,負(fù)值下偏
NSObliquenessAttributeName 設(shè)置字形傾斜度该默,取值為 NSNumber (float),正值右傾瞳氓,負(fù)值左傾
NSExpansionAttributeName 設(shè)置文本橫向拉伸屬性,取值為 NSNumber (float),正值橫向拉伸文本栓袖,負(fù)值橫向壓縮文本
NSWritingDirectionAttributeName 設(shè)置文字書寫方向匣摘,從左向右書寫或者從右向左書寫
NSVerticalGlyphFormAttributeName 設(shè)置文字排版方向,取值為 NSNumber 對(duì)象(整數(shù))裹刮,0 表示橫排文本音榜,1 表示豎排文本
NSLinkAttributeName 設(shè)置鏈接屬性,點(diǎn)擊后調(diào)用瀏覽器打開指定URL地址
NSAttachmentAttributeName 設(shè)置文本附件,取值為NSTextAttachment對(duì)象,常用于文字圖片混排
NSParagraphStyleAttributeName 設(shè)置文本段落排版格式捧弃,取值為 NSParagraphStyle 對(duì)象