Loading...
Loading...
Compare original and translation side by side
// iOS/macOS
let descriptor = UIFontDescriptor(fontAttributes: [
.family: "SF Pro",
kCTFontWidthTrait: 1.0 // 1.0 = Expanded
])// iOS/macOS
let descriptor = UIFontDescriptor(fontAttributes: [
.family: "SF Pro",
kCTFontWidthTrait: 1.0 // 1.0 = Expanded
])"TextKit 2 abstracts away glyph handling to provide a consistent experience for international text."
"TextKit 2 抽象了字形处理逻辑,为国际化文本提供一致的显示体验。"
| Text Style | Default Size (iOS) | Use Case |
|---|---|---|
| 34pt | Primary page headings |
| 28pt | Secondary headings |
| 22pt | Tertiary headings |
| 20pt | Quaternary headings |
| 17pt (Semibold) | Emphasized body text |
| 17pt | Primary body text |
| 16pt | Secondary body text |
| 15pt | Tertiary body text |
| 13pt | Footnotes, captions |
| 12pt | Small annotations |
| 11pt | Smallest annotations |
| 文本样式 | iOS默认字号 | 使用场景 |
|---|---|---|
| 34pt | 页面主标题 |
| 28pt | 二级标题 |
| 22pt | 三级标题 |
| 20pt | 四级标题 |
| 17pt(Semibold) | 强调型正文文本 |
| 17pt | 主要正文文本 |
| 16pt | 次要正文文本 |
| 15pt | 三级正文文本 |
| 13pt | 脚注、说明文字 |
| 12pt | 小型注释文本 |
| 11pt | 最小尺寸注释文本 |
.bold// UIKit
let descriptor = UIFontDescriptor.preferredFontDescriptor(withTextStyle: .title1)
let boldDescriptor = descriptor.withSymbolicTraits(.traitBold)!
let font = UIFont(descriptor: boldDescriptor, size: 0)
// SwiftUI
Text("Bold Title")
.font(.title.bold()).bold// UIKit
let descriptor = UIFontDescriptor.preferredFontDescriptor(withTextStyle: .title1)
let boldDescriptor = descriptor.withSymbolicTraits(.traitBold)!
let font = UIFont(descriptor: boldDescriptor, size: 0)
// SwiftUI
Text("Bold Title")
.font(.title.bold())// UIKit
let descriptor = UIFontDescriptor.preferredFontDescriptor(withTextStyle: .body)
let tightDescriptor = descriptor.withSymbolicTraits(.traitTightLeading)!
// SwiftUI
Text("Compact text")
.font(.body.leading(.tight))// SwiftUI
Text("Spacious paragraph")
.font(.body.leading(.loose))// UIKit
let descriptor = UIFontDescriptor.preferredFontDescriptor(withTextStyle: .body)
let tightDescriptor = descriptor.withSymbolicTraits(.traitTightLeading)!
// SwiftUI
Text("Compact text")
.font(.body.leading(.tight))// SwiftUI
Text("Spacious paragraph")
.font(.body.leading(.loose))// UIKit - UIFontMetrics
let customFont = UIFont(name: "Avenir-Medium", size: 34)!
let bodyMetrics = UIFontMetrics(forTextStyle: .body)
let scaledFont = bodyMetrics.scaledFont(for: customFont)
// Also scale constants
let spacing = bodyMetrics.scaledValue(for: 20.0)// SwiftUI - .font(.custom(_:relativeTo:))
Text("Custom scaled text")
.font(.custom("Avenir-Medium", size: 34, relativeTo: .body))
// @ScaledMetric for values
@ScaledMetric(relativeTo: .body) var padding: CGFloat = 20// UIKit - UIFontMetrics
let customFont = UIFont(name: "Avenir-Medium", size: 34)!
let bodyMetrics = UIFontMetrics(forTextStyle: .body)
let scaledFont = bodyMetrics.scaledFont(for: customFont)
// 同时缩放常量值
let spacing = bodyMetrics.scaledValue(for: 20.0)// SwiftUI - .font(.custom(_:relativeTo:))
Text("Custom scaled text")
.font(.custom("Avenir-Medium", size: 34, relativeTo: .body))
// 使用@ScaledMetric处理数值
@ScaledMetric(relativeTo: .body) var padding: CGFloat = 20// UIKit
textView.allowsDefaultTightening(for: .byTruncatingTail)
// SwiftUI
Text("Long text that needs to fit")
.lineLimit(1)
.minimumScaleFactor(0.5) // Allows tight tracking// UIKit
let attributes: [NSAttributedString.Key: Any] = [
.font: UIFont.preferredFont(forTextStyle: .body),
.kern: 2.0 // 2pt tracking
]
// SwiftUI
Text("Tracked text")
.tracking(2.0)
.kerning(2.0) // Alternative API.tracking().kerning()// UIKit
textView.allowsDefaultTightening(for: .byTruncatingTail)
// SwiftUI
Text("Long text that needs to fit")
.lineLimit(1)
.minimumScaleFactor(0.5) // 允许紧凑字距调整// UIKit
let attributes: [NSAttributedString.Key: Any] = [
.font: UIFont.preferredFont(forTextStyle: .body),
.kern: 2.0 // 2pt字距
]
// SwiftUI
Text("Tracked text")
.tracking(2.0)
.kerning(2.0) // 替代API.tracking().kerning()"Automatic line height adjustment for scripts with variable heights"
// UIKit
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineSpacing = 8.0 // 8pt additional space
// SwiftUI
Text("Custom spacing")
.lineSpacing(8.0)"为具有可变高度的脚本自动调整行高"
// UIKit
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineSpacing = 8.0 // 额外增加8pt行间距
// SwiftUI
Text("Custom spacing")
.lineSpacing(8.0)let attributes: [String: Any] = [
kCTFontOpticalSizeAttribute as String: pointSize
]
let descriptor = CTFontDescriptorCreateWithAttributes(attributes as CFDictionary)
let font = CTFontCreateWithFontDescriptor(descriptor, pointSize, nil)let attributes: [String: Any] = [
kCTFontOpticalSizeAttribute as String: pointSize
]
let descriptor = CTFontDescriptorCreateWithAttributes(attributes as CFDictionary)
let font = CTFontCreateWithFontDescriptor(descriptor, pointSize, nil)AttributedStringTextlineHeightMultiple"TextEditor substitutes the default value calculated from the environment for any AttributedStringKeys with a value of nil."
TextAttributedStringTextAttributedStringlineHeightMultiple"对于值为nil的AttributedStringKeys,TextEditor会使用从环境计算出的默认值进行替换。"
TextAttributedString// ❌ WRONG - .font() modifier can override and drop paragraph styles
var s = AttributedString(longString)
// Set paragraph style
var p = AttributedString.ParagraphStyle()
p.lineHeightMultiple = 0.92
s.paragraphStyle = p
// ⚠️ No font set in AttributedString
Text(s)
.font(.body) // ⚠️ May rebuild runs, lose lineHeightMultipleAttributedStringnil.font(.body)// ❌ 错误写法 - .font()修饰符会覆盖并丢失段落样式
var s = AttributedString(longString)
// 设置段落样式
var p = AttributedString.ParagraphStyle()
p.lineHeightMultiple = 0.92
s.paragraphStyle = p
// ⚠️ 未在AttributedString中设置字体
Text(s)
.font(.body) // ⚠️ 可能重新构建文本流,丢失lineHeightMultipleAttributedStringnil.font(.body)// ✅ CORRECT - Font in AttributedString, no environment override
var s = AttributedString(longString)
// Set font INSIDE the attributed content
s.font = .system(.body) // ✅ Typography inside AttributedString
// Set paragraph style
var p = AttributedString.ParagraphStyle()
p.lineHeightMultiple = 0.92
s.paragraphStyle = p
Text(s) // ✅ No .font() modifiernil.font()// ✅ 正确写法 - 字体在AttributedString中,不使用环境覆盖
var s = AttributedString(longString)
// 在富文本内容内部设置字体
s.font = .system(.body) // ✅ 排版属性包含在AttributedString中
// 设置段落样式
var p = AttributedString.ParagraphStyle()
p.lineHeightMultiple = 0.92
s.paragraphStyle = p
Text(s) // ✅ 不添加.font()修饰符nil.font()var s = AttributedString("Carefully styled text")
s.font = .system(.body)
var p = AttributedString.ParagraphStyle()
p.lineHeightMultiple = 0.92
p.alignment = .leading
s.paragraphStyle = p
Text(s) // No modifierTextTextEditorvar s = AttributedString("Carefully styled text")
s.font = .system(.body)
var p = AttributedString.ParagraphStyle()
p.lineHeightMultiple = 0.92
p.alignment = .leading
s.paragraphStyle = p
Text(s) // 不添加修饰符TextTextEditorText("Simple text")
.font(.body)
.lineSpacing(4.0) // SwiftUI-level spacingText("Simple text")
.font(.body)
.lineSpacing(4.0) // SwiftUI层级的行间距var s = AttributedString("Title")
s.font = .system(.title).bold()
var body = AttributedString(" and body text")
body.font = .system(.body)
s.append(body)
Text(s) // ✅ No .font() modifier preserves both fontsvar s = AttributedString("Title")
s.font = .system(.title).bold()
var body = AttributedString(" and body text")
body.font = .system(.body)
s.append(body)
Text(s) // ✅ 不添加.font()修饰符可保留两种字体// ❌ WRONG mental model: "Create AttributedString first"
var s = AttributedString(text)
var p = AttributedString.ParagraphStyle()
p.lineHeightMultiple = 0.92
s.paragraphStyle = p
s.font = .system(.body) // ⚠️ Setting font last doesn't help if you use .font() modifier
Text(s).font(.body) // Still breaks!AttributedString.font(...)// ❌ 错误认知:「先创建AttributedString再设置属性」
var s = AttributedString(text)
var p = AttributedString.ParagraphStyle()
p.lineHeightMultiple = 0.92
s.paragraphStyle = p
s.font = .system(.body) // ⚠️ 即使最后设置字体,使用.font()修饰符仍会失效
Text(s).font(.body) // 仍然会丢失样式!AttributedString.font(...)AttributedStringAttributedStringnil.font()TextAttributedStringnilText.font().lineLimit(nil).lineLimit(2...5).minimumScaleFactor().lineLimit(nil).lineLimit(2...5).minimumScaleFactor()font-family: system-ui; /* SF Pro */
font-family: ui-rounded; /* SF Pro Rounded */
font-family: ui-serif; /* New York */
font-family: ui-monospace; /* SF Mono */font-family: -apple-system; /* deprecated, use system-ui */font-family: system-ui; /* SF Pro */
font-family: ui-rounded; /* SF Pro Rounded */
font-family: ui-serif; /* New York */
font-family: ui-monospace; /* SF Mono */font-family: -apple-system; /* 已废弃,建议使用system-ui */Text("Recipe Editor")
.font(.largeTitle.bold()) // Emphasized variantText("Recipe Editor")
.font(.largeTitle.bold()) // 强调变体let customFont = UIFont(name: "Avenir-Medium", size: 17)!
let metrics = UIFontMetrics(forTextStyle: .body)
label.font = metrics.scaledFont(for: customFont)
label.adjustsFontForContentSizeCategory = truelet customFont = UIFont(name: "Avenir-Medium", size: 17)!
let metrics = UIFontMetrics(forTextStyle: .body)
label.font = metrics.scaledFont(for: customFont)
label.adjustsFontForContentSizeCategory = truelet descriptor = UIFontDescriptor
.preferredFontDescriptor(withTextStyle: .largeTitle)
.withDesign(.rounded)!
let font = UIFont(descriptor: descriptor, size: 0)let descriptor = UIFontDescriptor
.preferredFontDescriptor(withTextStyle: .largeTitle)
.withDesign(.rounded)!
let font = UIFont(descriptor: descriptor, size: 0)Text("Today")
.font(.largeTitle.bold())
.fontDesign(.rounded)Text("Today")
.font(.largeTitle.bold())
.fontDesign(.rounded)struct RecipeView: View {
@ScaledMetric(relativeTo: .body) var padding: CGFloat = 20
var body: some View {
Text("Recipe")
.padding(padding) // Scales with Dynamic Type
}
}struct RecipeView: View {
@ScaledMetric(relativeTo: .body) var padding: CGFloat = 20
var body: some View {
Text("Recipe")
.padding(padding) // 随Dynamic Type缩放
}
}