UILabel行間距

來源:HarrisonXi

最近準(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)暖庄,示意代碼:

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];

運(yùn)行一下觀察效果:

雖然用我們的眼睛看上去好像沒什么問題,但是設(shè)計(jì)師的火眼金睛一下就能看出來楼肪,和設(shè)計(jì)稿要求的有差距:

怎么會(huì)成這樣E嗬?這跟說好的不一樣對(duì)不對(duì)4航小肩钠?不要慌,我來細(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)的自帶邊距:

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];

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

關(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)唠粥,示意代碼:

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];

運(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)一二)舅巷。最終的代碼示例如下:

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];

貼一下在不同字號(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 懊锹健!情屹?坑爹呢這是F撼稹?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末垃你,一起剝皮案震驚了整個(gè)濱河市椅文,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌惜颇,老刑警劉巖皆刺,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異凌摄,居然都是意外死亡羡蛾,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門锨亏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來痴怨,“玉大人,你說我怎么就攤上這事器予±嗽澹” “怎么了?”我有些...
    開封第一講書人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵乾翔,是天一觀的道長(zhǎng)爱葵。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么钧惧? 我笑而不...
    開封第一講書人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任暇韧,我火速辦了婚禮,結(jié)果婚禮上浓瞪,老公的妹妹穿的比我還像新娘。我一直安慰自己巧婶,他們只是感情好乾颁,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著艺栈,像睡著了一般英岭。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上湿右,一...
    開封第一講書人閱讀 51,301評(píng)論 1 301
  • 那天诅妹,我揣著相機(jī)與錄音,去河邊找鬼毅人。 笑死吭狡,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的丈莺。 我是一名探鬼主播划煮,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼缔俄!你這毒婦竟也來了弛秋?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤俐载,失蹤者是張志新(化名)和其女友劉穎蟹略,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體遏佣,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡挖炬,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了贼急。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片茅茂。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖太抓,靈堂內(nèi)的尸體忽然破棺而出空闲,到底是詐尸還是另有隱情,我是刑警寧澤走敌,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布碴倾,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏跌榔。R本人自食惡果不足惜异雁,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望僧须。 院中可真熱鬧纲刀,春花似錦、人聲如沸担平。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽暂论。三九已至面褐,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間取胎,已是汗流浹背展哭。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留闻蛀,地道東北人匪傍。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像循榆,于是被迫代替她去往敵國(guó)和親析恢。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容