refer: Proportional vs. Monospaced Numbers: When to use which one in order to avoid “Wiggling Labels”

定义

等宽字体(英语:Monospaced Font)是指字符宽度相同的电脑字体。方块字基本上都作为等宽字体处理,如各个地区的汉字、日语假名的全角字符、韩语谚文音节等字符都是等宽的。 但是一些中文、日文字体中,由于同时涵盖西文字符,因此也含有比例字体,造成一个字体中两种类型混合的局面。

比例字体(英语:Proportional Font)是指字符宽度不尽相同的电脑字体。在传统西文活字印刷中使用的铅字,每个字母的字符宽度不太一样,如小写字母的i、j、l宽度明显较窄,而w、m明显较宽,但是这样做可以提高单词的可读性,这在铅字制作设计上称为比例字体。

如下图:
等宽字体monospaced
比例字体proportinal

倒计时文字闪烁

  • iOS 原生计时器
    timer

界面稳定且顺畅

  • 某款应用
    timer

数值闪烁,非常糟糕的实现

原因 :倒计时的文本使用了等宽与非等宽混合的字符

解决方案

首先可以确认上图的闪烁,是由于时间数值更新,虽然文本字数一样,但是宽度变了。所以,把数值部分的字体修改为等宽字体即可。当然需要尊重UI Designer,但是这也是最好的解决方案。

  • 使用系统字体
    UIFont.monospacedDigitSystemFont(ofSize: weight: )

iOS 9之后,系统字体默认是比例字体。用这个方法,在夹杂数值的文本中,会使用等宽的字体来展示文字

  • 使用UIFontDescriptor,定制字符的特性

把地址字符中的数字定制为等宽显示。使用feature type(featureIdentifier) kNumberSpacingTypeSelectors(typeIdentifier)kMonospacedNumbersSelector

import let CoreText.kMonospacedNumbersSelector
import let CoreText.kNumberSpacingType
extension UIFont {
    var monospacedDigitsFont: UIFont {
        let oldDescriptor = fontDescriptor
        let feature = UIFontDescriptor.FeatureKey.featureIdentifier
        let selector = UIFontDescriptor.FeatureKey.typeIdentifier
        let attr: [[UIFontDescriptor.FeatureKey: Any]] = [
            [feature: kNumberSpacingType, selector: kMonospacedNumbersSelector]
        ]
        let newDescriptor = oldDescriptor.addingAttributes([.featureSettings: attr])

        return UIFont(descriptor: newDescriptor, size: pointSize)
    }
}

如果有其它的定制需求,可以查开TrueType feature type。每一个feature type 都可以有不同的定制。例如: kStylisticAlternativesType 文字风格,kStylisticAltSevenOnSelector 即可以把 𝐚 -> 𝑎

最终结果

从效果可以看出,UIFont.monospacedDigitSystemFont(ofSize: weight: )数字是等宽字符,使用 不同字符集 ;使用UIFont.monospacedDigitsFont数字是等宽字符,使用 相同的字符集 ;使用UIFont.systemFont(ofSize:)数字是比例字符。

xx

但是也要注意,可能有些字体不支持等宽数字。这种情况下就可以使用NSAttributedString, 数字部分使用等宽字符。