核心速览: 在英制 Locale 下,180 cm 的桌子应显示为 5.9 ft,而人的身高应显示为 5 ft 11 in。不要手动编写单位换算逻辑,利用 MeasurementFormatStyle 的 usage 参数(如 .personHeight),系统会自动根据语义和区域设置输出最符合当地习惯的格式。
问题
在开发国际化 App 时,开发者经常面临一个棘手的“语境”问题:同一种物理量,在不同场景下的表述习惯完全不同。
最典型的例子是长度。假设数据库中存储的数值是 180,单位是 centimeters:
- 场景 A(家具尺寸): 如果是一张桌子,美国用户习惯看到 “5.9 ft”(小数形式)。
- 场景 B(人体身高): 如果是用户的身高,美国用户绝对期望看到 “5 ft 11 in”(混合单位),而不是怪异的 “5.9 ft”。
许多开发者为了解决这个问题,往往会编写复杂的 if-else 或手动操作 MeasurementFormatter,代码冗长且容易在边缘情况(如 1 ft 0 in 还是 1 ft)上出错。
解决方案
Swift 的 FormatStyle API 早已内置了“语义感知”能力。你不需要关心具体的数学换算,只需要通过 usage 参数告诉系统“这个数值是用来做什么的”。
系统会根据当前的 Locale 和你提供的 usage,自动做出以下决策:
- 单位选择:是使用公制(m/cm)还是英制(ft/in/mi)。
- 格式细节:是使用小数(5.9 ft)还是混合单位(5 ft 11 in)。
- 量级调整:对于道路距离,系统会自动根据数值大小决定显示米、千米还是英里。
代码实现
以下代码展示了如何利用 .measurement 修饰符处理不同的语义场景:
Swift
import SwiftUI
// 基础数据:180 厘米
let rawMeasurement = Measurement(value: 180, unit: UnitLength.centimeters)
struct MeasurementExample: View {
var body: some View {
VStack(alignment: .leading, spacing: 20) {
// asProvided 180cm
LabeledContent("asProvided", value: rawMeasurement,
format: .measurement(width: .abbreviated,usage: .asProvided))
.environment(\.locale, .init(identifier: "en_US"))
// Person's Height 5ft, 11in
LabeledContent("Person Height", value: rawMeasurement,
format: .measurement(width: .abbreviated, usage: .personHeight))
.environment(\.locale, .init(identifier: "en_US"))
// General 5.9ft
LabeledContent("General", value: rawMeasurement,
format: .measurement(width: .abbreviated, usage: .general))
.environment(\.locale, .init(identifier: "en_US"))
}
.padding(30)
.font(.title)
}
}
注意事项
- UI 布局的弹性:
personHeight在英制环境下生成的字符串(如 “5 ft 11 in”)通常比公制(“180 cm”)更长。在设计 UI 时(尤其是列表或卡片布局),务必预留足够的水平空间,或使用ViewThatFits来处理潜在的文字截断。 - 系统主导权:
当你使用
usage参数(特别是.road)时,你将“显示什么单位”的控制权完全交给了 Apple。对于很大的厘米数值,系统可能会直接格式化为“公里”或“英里”。如果你有硬性业务指标必须显示特定单位(例如必须显示“米”),请不要使用usage,而应先使用.converted(to:)转换单位后再进行基础格式化。
延伸阅读
MeasurementFormatStyle 只是 Swift 强大的度量值处理系统的一部分。如果你想深入了解如何处理自定义单位、对比新旧 Formatter 的性能差异,或者掌握更多像 usage 这样鲜为人知但极其实用的参数,建议阅读深度解析: