如何统一两端开发中的文本行高

Apr 8, 2020   #程序和设计 #设计规范 #设计细节

之前写过一个《为什么字体开发间距总是不等于设计间距?》;只介绍了行高不一致的原因,以及Android和iOS插件处理行高的方式,安卓字体太多暂时没做解析。

但常常我们都是以iOS为准一稿适配两端,那么如何让两端开发中的字高统一呢?一般我们会以设置行高规范的方式来解决,但其实我们知道安卓字体就是个大杂烩,一些特殊的字体可能会和我们设计稿中的字体相差几个像素高度;由于开发方式的不同几个像素的差异在走查过程中很可能就会翻车。

发现了问题

我们在校验的过程中会发现,安卓有时会和我们的设计稿对不上甚至间距越差越远,iOS可能有时也会,这个时候我们可能就会要求开发调整修改几个像素之类的,但是修改之后开发的间距和我们设计稿还一致吗?和我们的规范还一致吗?出现这个问题的原因就是我们没把规范严格化,也许我们没严格执行,也许开发没严格执行,也许开发和我们实现方式不一致。

说这些是啰嗦吗,肯定是一方出了问题呀,但当我们两方都没出问题,实现的界面还是没对上?头疼不头疼?

还是有问题!问题出在哪里?

我们发现定了行高规范后仍没达到我们想要的效果,如何才能像素级还原,以最优的方案解决问题,和开发在工作之余联调讨论了一个周解决方案后,我们试出个设计还原度最高的方案,并且团队开发侧和设计侧已投入使用,可供参考。

两方不一致

开发对设计不了解实现方式和设计不一致;

开发调用旧代码无法和新设计稿统一;

行高处理方式

由于开发习惯的问题;单行文字实现方式一般是,系统默认行高/去行高的方式/固定或相对行高:

多行文字对应实现方式,设置行间距/相对行高;

以上几种方式是设计中常用方式也是对应移动开发中常用解决方案;了解开发方式会更有助于我们校验设计的还原度。

反求相对行高

实际案例示例分析:

以上为设计稿和iOS侧的处理方式,Web,Android和iOS对字体的处理原理有所不同,但不影响我们找一最优解决方案,相对于Web端由于适配问题则更建议使用相对行高。

以上可总结为:

单行 默认字体行高/去行高,对应多行 固定行高/相对行高/行间距;

字体中的问题

iOS

我们知道目前iOS systemFont字体中 中文使用的是苹方字体,英文和数字使用SF Pro Text ;

之前有个计算默认行高的插件和公式:

* 公式来自 支付宝体验设计精髓 一书

我们来测试一下看是否准确,找一组常用字号来测试:

拿字号56来测试

计算字高为68px;测试结果单行效果完全一致;对其他字号全部跑一遍结果也是一致的,就不一一列举了。

测试多行

在多行测试中和设计稿不太一致。

我们用56px字,默认行高为68px;行间距设置为22px;在sketch中文本控件高度为68*3+22*2=248px

但我们预览控件的时候发现却不是这个高度:

这个高度是123也就是246px;和248px差了2px;我们尝试测试更多行,发现行数越多和设计高度相差越多,这是为什么呢?

看这张图其实就明白了

其实系统的行高是一个六位小数,这也就验证了前边那个求行高公式为什么向上取整了,我们看33.414062向上取整为34则行高为68px,两行在设计稿中为136px;33.414062两行66.828124向上取整为67则行高为134px;和设计稿差2px;行数越多则最终结果和设计稿相差越大,不同字号也有所差异;因此以上算行高公式对单行文本是正确的,多行则不能那么算。

Android在Android平台也是类似此类的算法,但是安卓中会给字体特殊加padding,就导致了安卓默认高度比iOS默认高度高一些,具体高多少也是没有规律的,所以我们出一稿适配两端的时候校验起来特别费劲。

安卓处理行高的方式有三种,设置固定行高/删去上下padding/只保留字高。

效果是这样的:

这里的只保留字高并非是我们设计稿中字号设置多少行高设置多少,而是根据文字绘制的最高点和最低点。

加这行代码去除上下padding

android:includeFontPadding=”false

由以上,安卓系统默认行高无法和iOS一致,但是去除padding后行高和iOS默认行高非常接近,于是我们测试了一下:

测试 机型 小米(720*1280/1080*1920/1440*2560),华为(1080*1920),vivo(2080*1920/1440*2560)。

在测试中发现去掉padding后的行高非常接近iOS默认字体高度,根据字体大小会稍有差异,但是可以这么理解,相同字号中安卓的字体如果比iOS大一些行高就会稍高一点,这么计算也理所应当即使从设计侧如果字号稍大一点我们也应该让空间留的稍大一点,当然这只是一两个像素的差异。

总结字体问题

iOS:可设置固定行高(相对行高)/系统默认行高/去行高(行高等于字号)

Android:可设置固定行高(相对行高)/系统默认行高/去上下padding/保留字高

如何让它俩统一到一块呢,

如果都设置固定行高或相对行高,相同字号中随安卓字体的不同字体稍大会稍挤字体稍小会稍散的感觉,但也不是不可取看能接受的程度。

系统默认行高,两端没法统一,我们没法确定以及统一安卓各个厂商的字体。设计中常常会把行高设为和字号一致,iOS去行高,但在安卓中没法算出和苹方去行高一致的算法,行高和字号设为一致容易翻车,在某些情况下开发设置了控件高度和行高一致的话有些字体是无法显示完的,或者后期调整字体就会有无法显示完的风险。

当然安卓中的保留字高更不可取我们没法这样出设计稿,仅在一个平台以怎样的方式都可以对接。

当使用一稿对接两端的情况,在测试过程中,系统的才是最好的。

首先,如果我们是以iOS规范出设计稿。字体使用的是iOS的系统字体,那么我们就理所应当遵循iOS系统规范去做设计,我们经常感觉开发效果和设计稿差异太大,其实如果两者遵循相同的规范设置参数一致实现效果就应该一致。对于安卓,经测试安卓去除padding后和iOS默认行高最接近,并且使用系统字体,对于安卓校验,我们校验的时候应该是看其参数是否一致。

最终我们采用的方案是:设计稿以iOS系统字体规范出,也就是以上边那个公式算出来的行高,安卓去除includeFontPadding和苹方字体最接近,行间距不用固定行高或相对行高,使用linespace行间距。

在尝试过程中也找到了iOS和设计稿单行多行都完全一致的方案,但需要引用用户字体,也就是非系统字体,我们将苹方字体打包至应用内,对三方字体进行测试发现,打包的苹方行高没有规律:

可以给不同字号设置固定行高,例如我们把引用的苹方设置成和系统行高一样的:

这时我们发现文字显示的位置不对,需要使用baselineOffset属性修正文字在行中的显示位置:

CGFloatbaselineOffset = (lineHeight - label.font.lineHeight) / 2;

多行里采用的方式其实就1*n的方式绘制多个单行文本最终高度和设计稿是完全一致的,但是该方案并没有采用,我们知道使用三方苹方字体在英文和数字中就没法获取系统的SF字体,会造成字体不统一。