Loading...
Loading...
Compare original and translation side by side
FormatStyleFormatterFormatStyleFormatter| Type | Style Access | Example |
|---|---|---|
| | |
| Currency | | |
| Percent | | |
| | |
| Date range | | |
| Relative date | | |
| | |
| | |
| | |
| | |
| | |
| Byte count | | |
| | |
| 类型 | 样式访问方式 | 示例 |
|---|---|---|
| | |
| 货币 | | |
| 百分比 | | |
| | |
| 日期范围 | | |
| 相对日期 | | |
| | |
| | |
| | |
| | |
| | |
| 字节数 | | |
| | |
// Default locale-aware formatting
let n = 1234567.formatted() // "1,234,567" (en_US)
// Precision
1234.5.formatted(.number.precision(.fractionLength(0...2))) // "1,234.5"
1234.5.formatted(.number.precision(.significantDigits(3))) // "1,230"
// Rounding
1234.formatted(.number.rounded(rule: .down, increment: 100)) // "1,200"
// Grouping
1234567.formatted(.number.grouping(.never)) // "1234567"
// Notation
1_200_000.formatted(.number.notation(.compactName)) // "1.2M"
42.formatted(.number.notation(.scientific)) // "4.2E1"
// Sign display
(-42).formatted(.number.sign(strategy: .always())) // "+42" / "-42"
// Locale override
42.formatted(.number.locale(Locale(identifier: "de_DE"))) // "42"// 默认区域感知格式化
let n = 1234567.formatted() // "1,234,567" (en_US)
// 精度设置
1234.5.formatted(.number.precision(.fractionLength(0...2))) // "1,234.5"
1234.5.formatted(.number.precision(.significantDigits(3))) // "1,230"
// 舍入规则
1234.formatted(.number.rounded(rule: .down, increment: 100)) // "1,200"
// 分组设置
1234567.formatted(.number.grouping(.never)) // "1234567"
// 表示法
1_200_000.formatted(.number.notation(.compactName)) // "1.2M"
42.formatted(.number.notation(.scientific)) // "4.2E1"
// 符号显示
(-42).formatted(.number.sign(strategy: .always())) // "+42" / "-42"
// 区域覆盖
42.formatted(.number.locale(Locale(identifier: "de_DE"))) // "42"29.99.formatted(.currency(code: "USD")) // "$29.99"
29.99.formatted(.currency(code: "EUR")) // "€29.99"
29.99.formatted(.currency(code: "JPY")) // "¥30"
// Customize precision
let style = FloatingPointFormatStyle<Double>.Currency(code: "USD")
.precision(.fractionLength(0))
1234.56.formatted(style) // "$1,235"29.99.formatted(.currency(code: "USD")) // "$29.99"
29.99.formatted(.currency(code: "EUR")) // "€29.99"
29.99.formatted(.currency(code: "JPY")) // "¥30"
// 自定义精度
let style = FloatingPointFormatStyle<Double>.Currency(code: "USD")
.precision(.fractionLength(0))
1234.56.formatted(style) // "$1,235"0.85.formatted(.percent) // "85%"
0.8567.formatted(.percent.precision(.fractionLength(1))) // "85.7%"
42.formatted(.percent) // "42%" (integer)0.85.formatted(.percent) // "85%"
0.8567.formatted(.percent.precision(.fractionLength(1))) // "85.7%"
42.formatted(.percent) // "42%" (整数)let now = Date.now
// Components
now.formatted(.dateTime.year().month().day()) // "Apr 22, 2026"
now.formatted(.dateTime.hour().minute()) // "4:30 PM"
now.formatted(.dateTime.weekday(.wide).month(.wide).day()) // "Wednesday, April 22"
// Predefined styles
now.formatted(date: .long, time: .shortened) // "April 22, 2026 at 4:30 PM"
now.formatted(date: .abbreviated, time: .omitted) // "Apr 22, 2026"
// ISO 8601
now.formatted(.iso8601) // "2026-04-22T16:30:00Z"
// Relative
let yesterday = Calendar.current.date(byAdding: .day, value: -1, to: .now)!
yesterday.formatted(.relative(presentation: .named)) // "yesterday"
yesterday.formatted(.relative(presentation: .numeric)) // "1 day ago"
// Interval
(date1..<date2).formatted(.interval.month().day().hour().minute())
// Components (countdown-style)
(date1..<date2).formatted(.components(style: .wide, fields: [.day, .hour]))
// "2 days, 5 hours"let now = Date.now
// 组件设置
now.formatted(.dateTime.year().month().day()) // "Apr 22, 2026"
now.formatted(.dateTime.hour().minute()) // "4:30 PM"
now.formatted(.dateTime.weekday(.wide).month(.wide).day()) // "Wednesday, April 22"
// 预定义样式
now.formatted(date: .long, time: .shortened) // "April 22, 2026 at 4:30 PM"
now.formatted(date: .abbreviated, time: .omitted) // "Apr 22, 2026"
// ISO 8601 格式
now.formatted(.iso8601) // "2026-04-22T16:30:00Z"
// 相对日期
let yesterday = Calendar.current.date(byAdding: .day, value: -1, to: .now)!
yesterday.formatted(.relative(presentation: .named)) // "yesterday"
yesterday.formatted(.relative(presentation: .numeric)) // "1 day ago"
// 时间间隔
(date1..<date2).formatted(.interval.month().day().hour().minute())
// 组件(倒计时样式)
(date1..<date2).formatted(.components(style: .wide, fields: [.day, .hour]))
// "2 days, 5 hours"Date.AnchoredRelativeFormatStyleDate.AnchoredRelativeFormatStyleDurationDurationlet d = Duration.seconds(3661)
d.formatted(.time(pattern: .hourMinuteSecond)) // "1:01:01"
d.formatted(.time(pattern: .hourMinute)) // "1:01"
d.formatted(.time(pattern: .minuteSecond)) // "61:01"
// Fractional seconds
Duration.seconds(3.75).formatted(
.time(pattern: .minuteSecond(padMinuteToLength: 2, fractionalSecondsLength: 2))
) // "00:03.75"let d = Duration.seconds(3661)
d.formatted(.time(pattern: .hourMinuteSecond)) // "1:01:01"
d.formatted(.time(pattern: .hourMinute)) // "1:01"
d.formatted(.time(pattern: .minuteSecond)) // "61:01"
// 小数秒
Duration.seconds(3.75).formatted(
.time(pattern: .minuteSecond(padMinuteToLength: 2, fractionalSecondsLength: 2))
) // "00:03.75"Duration.seconds(3661).formatted(
.units(allowed: [.hours, .minutes, .seconds], width: .abbreviated)
) // "1 hr, 1 min, 1 sec"
Duration.seconds(90).formatted(
.units(allowed: [.minutes, .seconds], width: .wide)
) // "1 minute, 30 seconds"
Duration.seconds(90).formatted(
.units(allowed: [.minutes, .seconds], width: .narrow)
) // "1m 30s"
// Limit unit count
Duration.seconds(3661).formatted(
.units(allowed: [.hours, .minutes, .seconds], width: .abbreviated, maximumUnitCount: 2)
) // "1 hr, 1 min"Duration.seconds(3661).formatted(
.units(allowed: [.hours, .minutes, .seconds], width: .abbreviated)
) // "1 hr, 1 min, 1 sec"
Duration.seconds(90).formatted(
.units(allowed: [.minutes, .seconds], width: .wide)
) // "1 minute, 30 seconds"
Duration.seconds(90).formatted(
.units(allowed: [.minutes, .seconds], width: .narrow)
) // "1m 30s"
// 限制单位数量
Duration.seconds(3661).formatted(
.units(allowed: [.hours, .minutes, .seconds], width: .abbreviated, maximumUnitCount: 2)
) // "1 hr, 1 min"let temp = Measurement(value: 72, unit: UnitTemperature.fahrenheit)
temp.formatted(.measurement(width: .wide)) // "72 degrees Fahrenheit"
temp.formatted(.measurement(width: .abbreviated)) // "72°F"
temp.formatted(.measurement(width: .narrow)) // "72°"
let dist = Measurement(value: 5, unit: UnitLength.kilometers)
dist.formatted(.measurement(width: .abbreviated, usage: .road)) // "3.1 mi" (en_US)let temp = Measurement(value: 72, unit: UnitTemperature.fahrenheit)
temp.formatted(.measurement(width: .wide)) // "72 degrees Fahrenheit"
temp.formatted(.measurement(width: .abbreviated)) // "72°F"
temp.formatted(.measurement(width: .narrow)) // "72°"
let dist = Measurement(value: 5, unit: UnitLength.kilometers)
dist.formatted(.measurement(width: .abbreviated, usage: .road)) // "3.1 mi" (en_US)var name = PersonNameComponents()
name.givenName = "Thomas"
name.familyName = "Clark"
name.middleName = "Louis"
name.namePrefix = "Dr."
name.nickname = "Tom"
name.nameSuffix = "Esq."
name.formatted(.name(style: .long)) // "Dr. Thomas Louis Clark Esq."
name.formatted(.name(style: .medium)) // "Thomas Clark"
name.formatted(.name(style: .short)) // "Tom"
name.formatted(.name(style: .abbreviated)) // "TC"var name = PersonNameComponents()
name.givenName = "Thomas"
name.familyName = "Clark"
name.middleName = "Louis"
name.namePrefix = "Dr."
name.nickname = "Tom"
name.nameSuffix = "Esq."
name.formatted(.name(style: .long)) // "Dr. Thomas Louis Clark Esq."
name.formatted(.name(style: .medium)) // "Thomas Clark"
name.formatted(.name(style: .short)) // "Tom"
name.formatted(.name(style: .abbreviated)) // "TC"["Alice", "Bob", "Charlie"].formatted(.list(type: .and))
// "Alice, Bob, and Charlie"
["Alice", "Bob", "Charlie"].formatted(.list(type: .or))
// "Alice, Bob, or Charlie"
// With member formatting
[1, 2, 3].formatted(.list(memberStyle: .number, type: .and))
// "1, 2, and 3"
// Narrow width
["A", "B", "C"].formatted(.list(type: .and, width: .narrow))
// "A, B, C"["Alice", "Bob", "Charlie"].formatted(.list(type: .and))
// "Alice, Bob, and Charlie"
["Alice", "Bob", "Charlie"].formatted(.list(type: .or))
// "Alice, Bob, or Charlie"
// 成员格式化
[1, 2, 3].formatted(.list(memberStyle: .number, type: .and))
// "1, 2, and 3"
// 窄宽度
["A", "B", "C"].formatted(.list(type: .and, width: .narrow))
// "A, B, C"Int64(1_048_576).formatted(.byteCount(style: .memory)) // "1 MB"
Int64(1_048_576).formatted(.byteCount(style: .file)) // "1 MB"
Int64(1_048_576).formatted(.byteCount(style: .binary)) // "1 MiB"Int64(1_048_576).formatted(.byteCount(style: .memory)) // "1 MB"
Int64(1_048_576).formatted(.byteCount(style: .file)) // "1 MB"
Int64(1_048_576).formatted(.byteCount(style: .binary)) // "1 MiB"let url = URL(string: "https://example.com/path?q=1")!
url.formatted()
// "https://example.com/path?q=1"
url.formatted(.url.scheme(.never).host().path())
// "example.com/path"
url.formatted(.url.scheme(.always).host(.never).path())
// "https:///path"let url = URL(string: "https://example.com/path?q=1")!
url.formatted()
// "https://example.com/path?q=1"
url.formatted(.url.scheme(.never).host().path())
// "example.com/path"
url.formatted(.url.scheme(.always).host(.never).path())
// "https:///path"Textformat:// Inline format style
Text(price, format: .currency(code: "USD"))
Text(date, format: .dateTime.month().day().year())
Text(duration, format: .units(allowed: [.minutes, .seconds]))
// Timer-style (live updating)
Text(.now, style: .timer)
Text(.now, style: .relative)
Text(timerInterval: start...end)Text(_:format:)Textformat:// 内联格式样式
Text(price, format: .currency(code: "USD"))
Text(date, format: .dateTime.month().day().year())
Text(duration, format: .units(allowed: [.minutes, .seconds]))
// 计时器样式(实时更新)
Text(.now, style: .timer)
Text(.now, style: .relative)
Text(timerInterval: start...end)Text(_:format:)FormatStyleParseableFormatStylestruct AbbreviatedCountStyle: FormatStyle {
func format(_ value: Int) -> String {
switch value {
case ..<1_000:
return "\(value)"
case 1_000..<1_000_000:
return String(format: "%.1fK", Double(value) / 1_000)
default:
return String(format: "%.1fM", Double(value) / 1_000_000)
}
}
}
extension FormatStyle where Self == AbbreviatedCountStyle {
static var abbreviatedCount: AbbreviatedCountStyle { .init() }
}
// Usage
let followers = 12_500
Text(followers, format: .abbreviatedCount) // "12.5K"FormatStyleParseableFormatStylestruct AbbreviatedCountStyle: FormatStyle {
func format(_ value: Int) -> String {
switch value {
case ..<1_000:
return "\(value)"
case 1_000..<1_000_000:
return String(format: "%.1fK", Double(value) / 1_000)
default:
return String(format: "%.1fM", Double(value) / 1_000_000)
}
}
}
extension FormatStyle where Self == AbbreviatedCountStyle {
static var abbreviatedCount: AbbreviatedCountStyle { .init() }
}
// 使用示例
let followers = 12_500
Text(followers, format: .abbreviatedCount) // "12.5K"| Mistake | Fix |
|---|---|
Using legacy | Use |
String interpolation for formatted numbers in | Use |
| Hardcoding locale in format styles | Omit |
Using | Use |
Creating | FormatStyle instances are value types cached by Foundation; safe to create inline |
Formatting | Use |
Ignoring | Specify |
| 错误 | 修复方案 |
|---|---|
在新代码中使用旧版 | 使用 |
在 | 使用 |
| 在格式样式中硬编码区域设置 | 默认省略 |
使用 | 使用 |
在 | FormatStyle实例是值类型,由Foundation缓存;可安全地内联创建 |
使用 | 直接使用 |
忽略度量值的 | 指定 |
FormatStyleFormatterText(_:format:)Duration.TimeFormatStyleDuration.UnitsFormatStyleusage:CodableHashableFormatStyleFormatterText(_:format:)Duration.TimeFormatStyleDuration.UnitsFormatStyleusage:CodableHashable