ios 容器内容显示原理及调整
先发一行文字,看一下奇特之处:
09afgjkpqz汉字😸
这是编辑器自动加的横线iOS,OS文本容器中都是基线对齐,所谓的基线对齐就是指无论中文字符,数字,英文字符,表情等它们在一行的时候,基线是在同一高度的。图例中的红线就是基线。
1 2概念:
baseline: 相当于坐标原点。大部分的拉丁字母底部与此对齐,汉字的中下部与此对齐(这是设定)
ascent, descent: 相当于字体可绘制区域的上下最大值。根据自己的观察,ascent 并不一定是最高字符的高度(比如上图的 f),在大部分字体中,ascent 会比最高的字符还要高一些,上面会有个空间。 desecnt 同理。(descent 为负值)
leading: 即行间距。但这个行间距与平时所说的行间距并不是一个东西。在文本编辑器中,选择不同字体的时候,视觉上的每行的距离并不是一样的。有可能就是 leading 不同。这个值可能为 0。(对于 iOS 上的 SF 系列字体,它的值就是 0 )
line height: 即行高。它的值定义为 ascent + descent + leading。(descent 为负值,所以准确的写应该取绝对值)。这也是我们最关心的一个值。
这些值都是字体的属性,是字体的设计者制定的,不可变,不同的字体会不一样。
平时用来表示字体大小的「字号」并不对应上图中的任何值,也就没有一个直接的几何意义。字号准确的说法是 point size。对于一个 point size 是 15 的 SFUI 字体,它的 line height 为 17.900390625, 约为 point size 的 1.2 倍。所以对于这个字体的一行文字,它的行高为 17.900390625。如果硬要显示在 15.0 高度的矩形内,g 和 f 应该会显示不全。
行间距
line height 所代表的高度只是一行文字的高度。可以把一行文字看做以 line height 为高的矩形,多行文字就是这些矩形纵向排列。矩形的间距就是通常我们说的行间距:
而通常所说的「行间距」「行高」「line height mutiple」 这样的词语,描述的就是这个间距的大小。
「行间距」: 直接对应间距的值
「行高」: line height + 间距。可以认为是,除了首行与尾行,每行实际所占的高度
line height mutiple: 即是「 x 倍行高」中的数值。line_height_mutiple = 「行高」/ line_height
不同平台的实现效果
iOS
使用 autolayout 的一行 UIlabel 的高度即为所使用字体的 line height。但 autolayout 中,view 的 frame 的小数点精度会对齐到像素精度。所以 15 号字体的 label 高度为 18.0 point 。
对于多行文字的行间距,可以通过 attributedString 中的 paragraph style 来控制。paragraph style 可以设定如下值:
lineHeightMultiple: 同上面所说的。
minimumLineHeight/maximumLineHeight: 即「行高」
这两个值都会改变行高,只是写法不同而已。但使用它们控制行间距有一个问题,如果行高大于字体的 line height,那么多余的空间将会放在这行的上面: baseline 所在的位置是矩形底边 + ( leading + descent ) 的位置。一个常见的情况,圆形的 avatar 与右侧的 label 顶端对齐,如果使用 lineHeightMultiple,那么为了达到视觉上的对齐,avatar 与 label 的 frame.y 就会不一样。不是很理想。(在使用 insets 或 background color 的时候就会很麻烦)
lineSpacing: 即行间距。
使用 lineSpacing 只会在每行之间添加间距。在首行与尾行外侧并没有额外的空白(当然,line height 里所带的空白仍然存在)。比较符合我们行间距的设定,不存在上面提到的问题。但不同 point size 为了有同样的效果,需要设定不同的 lineSpacing,不如 lineHeightMultiple 使用方便。
实际使用:
4假设UI提供了这样一种效果,让开发来实现。
5你开发完之后发现是这样的。
这时候就需要设置富文本的NSBaselineOffsetAttributeName来实现不同字号的文字的垂直居中对齐了。
NSBaselineOffsetAttributeName:@(0.36*(a-b)),a是本行文本最大字号,b是当前文本段的字号。设置为就可以实现4的效果了。至于为什么用0.36这个神奇数字,暂时未知 ̄□ ̄||
6对于图6的难以理解的UI设计,实现亦可以通过设置NSBaselineOffsetAttributeName来实现。