开发者必看:如何在 iOS 应用中完美实现动态自定义字体!

这里每天分享一个 iOS 的新知识,快来关注我吧

前言

作为 App 开发来说,字体在应用中扮演着至关重要的角色。一个精心选择的字体能够让你的应用在众多竞争者中脱颖而出。

但是,无论你选择哪种字体,都必须确保它的核心功能------可读性。

在以前,只有苹果自带的系统字体支持动态调整大小,而自定义字体则不支持。但自从 iOS 11 以来,这种情况已经改变。现在,你可以轻松地在动态字体中使用你的自定义字体。

今天就来讲讲如何在动态字体中使用自定义字体。

什么是动态字体?

苹果早在 iOS 7 时就引入了动态字体,旨在让用户选择他们偏好的文本大小以满足自身需求。

在较大的文本尺寸下,各种文本样式(如 .headline, .subheadline, .body, .footnote, .caption1, .caption2, .largeTitle, .title1, .title2, .title3.callout)的权重、大小和行距值可以参考苹果的人机界面指南 - 动态字体尺寸[1]。

动态字体的实现

动态字体与文本样式一起起作用,文本样式用于为每种文本大小设定缩放因子。例如,.caption2 是最小的文本样式,不会缩小到小于 11 号的大小,因为那样会很难阅读。在最小、小、中和大文本大小下,.caption2 文本样式的大小将保持在 11pt。

要获取动态字体,我们可以使用 UIFont 类方法 preferredFont(forTextStyle:) 来初始化字体。

ini 复制代码
let label = UILabel()
label.font = UIFont.preferredFont(forTextStyle: .body)
label.adjustsFontForContentSizeCategory = true 

设置 adjustsFontForContentSizeCategory 为 true,可以在设备的内容大小类别更改时自动更新字体。

上面的代码将返回一个苹果 San Francisco 常规字体,大小为 17(大文本尺寸下的 body 样式),以下是大文本大小上的所有文本样式的示例。

调整字体大小

可以通过以下方式更改字体大小:

  1. 打开系统设置 - 显示与亮度 - 文字大小。

  2. 通过拖动滑块调整字体大小。

调整到更大的字体

通过上边的方法调整字体到一定大小就不能再大了,其实还有办法可以调到更大:

  1. 打开设置 - 辅助功能 - 显示与文字大小 - 更大字体。

  2. 打开更大字体开关。

  3. 通过拖动滑块调整字体大小。

调试阶段修改文本大小

在开发阶段,还可以直接从 Xcode 调整字体。

  1. 点击调试栏中的图标 Environment Overrides 按钮.

  2. 打开 Dynamic Type 开关.

  3. 通过拖动滑块调整字体大小。

使用自定义字体

在 iOS 11 中,苹果引入了 UIFontMetrics,使我们的代码更简单。通过它,我们可以创建指定文本样式的 UIFontMetrics,然后将自定义字体传递给 scaledFont(for:) 方法,以获得基于自定义字体的字体对象,具有适当的样式信息,并自动缩放以匹配当前动态字体设置。

ini 复制代码
let customFont = UIFont(name: "Merriweather-Regular", size: 17)! // <1>
let label = UILabel()
label.adjustsFontForContentSizeCategory = true
label.font = UIFontMetrics(forTextStyle: .body).scaledFont(for: customFont) // <2>

<1> 我们初始化了自定义字体。在这个例子中,我使用了 Google 字体的 Merriweather 字体。

<2> 我们定义了 UIFontMetrics 的 body 文本样式,然后用它来缩放我们的自定义字体。

支持自定义字体的动态类型

虽然 UIFontMetrics 可以减少我们在支持动态类型的自定义字体上的工作量,但它并不是万能的。有时候我们仍然需要做一些额外的工作。

scaledFont(for:) 方法会根据大文本尺寸的基础字体大小应用缩放因子。苹果在 iOS 人机界面指南中说明了系统字体的字体度量标准。你可以用它作为为每种文本样式定义自定义字体的起始。

以下是我基于苹果度量的简单实现:

php 复制代码
let customFonts: [UIFont.TextStyle: UIFont] = [
    .largeTitle: UIFont(name: "Merriweather-Regular", size: 34)!,
    .title1: UIFont(name: "Merriweather-Regular", size: 28)!,
    .title2: UIFont(name: "Merriweather-Regular", size: 22)!,
    .title3: UIFont(name: "Merriweather-Regular", size: 20)!,
    .headline: UIFont(name: "Merriweather-Bold", size: 17)!,
    .body: UIFont(name: "Merriweather-Regular", size: 17)!,
    .callout: UIFont(name: "Merriweather-Regular", size: 16)!,
    .subheadline: UIFont(name: "Merriweather-Regular", size: 15)!,
    .footnote: UIFont(name: "Merriweather-Regular", size: 13)!,
    .caption1: UIFont(name: "Merriweather-Regular", size: 12)!,
    .caption2: UIFont(name: "Merriweather-Regular", size: 11)!
]

extension UIFont {
    class func customFont(forTextStyle style: UIFont.TextStyle) -> UIFont {
        let customFont = customFonts[style]!
        let metrics = UIFontMetrics(forTextStyle: style)
        let scaledFont = metrics.scaledFont(for: customFont)
        
        return scaledFont
    }
}

UIFontMetrics(forTextStyle: style).scaledFont(for: customFont) 替换为 UIFont.customFont(forTextStyle: style) 并再次运行即可。

ini 复制代码
let styles: [UIFont.TextStyle] = [.largeTitle, .title1, .title2, .title3, .headline, .subheadline, .body, .callout, .footnote, .caption1, .caption2]
for style in styles {
    ...
    let label = UILabel()
    label.adjustsFontForContentSizeCategory = true
    label.text = String(describing: style)
    label.font = UIFont.customFont(forTextStyle: style)    
    ...
}

最后看下效果:

结论

UIFontMetrics 可以减少我们在让自定义字体支持动态类型时所需的工作量。另外我们可能需要花一些时间来微调基础字体以确保其在所有变体中都适合,在 UIFontMetrics 的帮助下,这个过程不算负责。

希望这能帮助你更好地在应用中运用自定义字体。关于自定义动态字体,你有什么看法吗?欢迎在评论区中留言交流。

参考资料

1

苹果的人机界面指南: developer.apple.com/design/huma...

这里每天分享一个 iOS 的新知识,快来关注我吧

本文同步自微信公众号 "iOS新知",每天准时分享一个新知识,这里只是同步,想要及时学到就来关注我吧!

相关推荐
瓜子三百克3 小时前
CALayer的异步处理
macos·ios·cocoa
吴Wu涛涛涛涛涛Tao3 小时前
一步到位:用 Very Good CLI × Bloc × go_router 打好 Flutter 工程地基
flutter·ios
kymjs张涛8 小时前
零一开源|前沿技术周报 #6
前端·ios·harmonyos
iOS阿玮8 小时前
AppStore教你一招免备案的骚操作!
uni-app·app·apple
与火星的孩子对话1 天前
Unity进阶课程【六】Android、ios、Pad 终端设备打包局域网IP调试、USB调试、性能检测、控制台打印日志等、C#
android·unity·ios·c#·ip
恋猫de小郭2 天前
Flutter Widget Preview 功能已合并到 master,提前在体验毛坯的预览支持
android·flutter·ios
点金石游戏出海2 天前
每周资讯 | Krafton斥资750亿日元收购日本动画公司ADK;《崩坏:星穹铁道》新版本首日登顶iOS畅销榜
游戏·ios·业界资讯·apple·崩坏星穹铁道
旷世奇才李先生2 天前
Swift 安装使用教程
开发语言·ios·swift
90后的晨仔2 天前
Xcode16报错: SDK does not contain 'libarclite' at the path '/Applicati
ios