← 所有文章

SF Pro:可變軸、光學尺寸與動態字型契約

SF Pro 自 2015 年 iOS 9 / OS X El Capitan 起便是 Apple 的系統字型,它是一款具有三條軸(字重、寬度、光學尺寸)的可變字型,並屬於精心設計的字型家族,包含無襯線體(SF Pro)、圓體變體(SF Pro Rounded)、等寬字型(SF Mono)、緊湊變體(SF Compact,用於 watchOS),以及一款襯線體搭檔(New York)1。此字型圍繞動態字型契約打造:SwiftUI 的十一種文字樣式(.largeTitle.title.body.callout.footnote 等)預設皆使用 SF Pro,並會在使用者於「設定」中變更偏好文字大小時自動縮放。

大多數應用程式只用了這十一種樣式中的一兩種,除預設行為外不理會動態字型,也從不觸碰可變軸。結果就是字型雖能運作,卻沒有說出平台的詞彙。本文將走過系統字型家族、可變軸、十一種語意樣式、動態字型契約,以及自訂字型需要明確縮放工作才能參與的情境。

TL;DR

  • SF Pro Variable 暴露三條軸:字重(wght)、寬度(wdth)與光學尺寸(opsz)2。光學尺寸根據點數大小自動處理;字重與寬度可透過 SwiftUI 的 Font.WeightFont.Width 定址。
  • 系統字型家族包含 SF Pro(預設)、SF Pro Rounded(友善的 UI 元素)、SF Mono(程式碼、技術性 UI)、SF Compact(watchOS、狹窄情境),以及 New York(用於編輯閱讀的襯線體搭檔)。
  • SwiftUI 的十一種文字樣式自動支援動態字型。.body 是安全預設;從 .largeTitle.caption2 涵蓋平台的階層。
  • 自訂字型預設不會隨動態字型縮放。請使用 Font.custom("Name", size: 16, relativeTo: .body) 讓字型加入動態字型縮放3
  • @Environment(\.dynamicTypeSize) 讓檢視能依當前文字大小調整版面;其值範圍從 .xSmall.accessibility5(共 12 種尺寸)4

系統字型家族

Apple 推出五個都參與系統字型體驗的字型家族。

SF Pro

每個 Apple 平台的預設字型。iPhone、iPad、Mac、Vision 在內文、標題與大多數 UI 上都使用 SF Pro。此字型擁有廣泛的語言涵蓋(拉丁、希臘、西里爾、阿拉伯、希伯來、天城文等),原生支援由右至左的版面,並為小型大寫、替代數字(基線型對表格型)與風格替代字提供精心設計的變體。

在 SwiftUI 中透過系統字型存取:

Text("Hello").font(.system(.body))                    // SF Pro by default
Text("Hello").font(.system(.body, weight: .semibold)) // SF Pro Semibold
Text("Hello").font(.system(.body, design: .default))  // explicit SF Pro

SF Pro Rounded

為友善、平易近人的 UI 設計的圓體變體:Apple Watch 複雜功能、健身圓環、地圖方向標示的部分、尋找,以及部分健康介面。圓潤的字形傳達柔軟感;請因應「語氣」而非單純為了「視覺變化」而使用。

Text("Hello").font(.system(.body, design: .rounded))

SF Mono

用於程式碼、終端機 UI 以及任何需要字元格對齊情境的等寬字型家族。SF Mono 是 Xcode 的預設字型、終端機應用程式等寬設定中的字型,以及任何要求 .monospaced 的 SwiftUI 檢視中的字型。

Text("let x = 42").font(.system(.body, design: .monospaced))

SF Compact

watchOS 上、tvOS 焦點高亮上,以及部分水平空間受限的 Mac 情境所使用的較窄家族。較窄的字形在縮減水平佔位的同時保留 x 高度,使其能在 SF Pro 會顯得擁擠的 Apple Watch 錶面尺寸下運作。

watchOS 應用程式透過系統字型自動使用 SF Compact;iOS 應用程式通常不需要明確要求。

New York

設計作為編輯閱讀搭檔的襯線體家族。New York 出現在 Books 的長篇文字、Notes 的手寫風格筆記中,並透過 .serif 設計在 SwiftUI 中現身。

Text("Long-form essay").font(.system(.body, design: .serif))

襯線體搭檔在應用程式 UI 中很罕見。請刻意伸手取用(閱讀模式、引用段落、文章本文),而非作為預設。

三條可變軸

SF Pro Variable 編碼了三條結合起來產生每個字符的軸:

字重(wght)

九種具名字重:.ultraLight.thin.light.regular.medium.semibold.bold.heavy.black。可變字型在它們之間連續插值,但 SwiftUI 的 API 暴露的是具名值。

Text("Heading").font(.system(.title, weight: .semibold))

字重傳達強調的階層:.regular 用於內文,.semibold.bold 用於標題,.medium 用於活躍的工具列項目,.light 用於去強調的標籤。語意樣式(.headline.subheadline)隨附合理的字重預設;只在語意樣式不是正確形狀時,才伸手取用明確字重。

寬度(wdth)

iOS 16+ API 中四種具名寬度:.compressed.condensed.standard.expanded。寬度軸影響水平佔位,而不改變字重或視覺特徵。請將其用於緊湊的 UI(項目眾多的導覽列)或視覺質感(想要更多水平存在感的展示尺寸標題)。

寬度透過 SwiftUI 的 .fontWidth(_:) 檢視修飾符套用(或在 Font 上以 .width(_:) 串接):

Text("Compressed")
    .font(.system(.title, weight: .bold))
    .fontWidth(.compressed)

寬度很少是內文的正確軸;它在字型本身就是設計一部分的展示尺寸下運作良好。

光學尺寸(opsz)

技術上最具野心的軸。SF Pro 過去的 SF Text 與 SF Display 變體現在已成為編碼於可變字型中的連續漸層。在 20 點以下的尺寸,系統會套用 SF Text 的光學調整(更寬的字距、稍重的筆畫、更開放的字腔)。在 20 點以上,SF Display 接手(更緊的間距、更精緻的比例)。轉換是平滑的。

opsz 軸是自動的。應用程式不會明確處理它;系統讀取所請求的點數大小,並解析為正確的光學尺寸。其涵義:小尺寸 UI 的字型與標題尺寸的同款字型有不同比例,而這是刻意設計。缺乏光學尺寸的自訂字型在某個尺寸下看起來合宜,在其他尺寸則不對勁;SF Pro 的自動處理完全避免了這個陷阱。

十一種文字樣式

SwiftUI 的 Font.TextStyle 定義了十一種都參與動態字型的語意樣式4:

樣式 預設大小(Large) 常見用途
.largeTitle 34 pt 頂層標題、主視覺文字
.title 28 pt 區段標題
.title2 22 pt 次區段標題
.title3 20 pt 次要標題
.headline 17 pt 強調內文、列表行標題
.body 17 pt 預設內文
.callout 16 pt 輔助內文、情境中的標題說明
.subheadline 15 pt 次要標題、後設文字
.footnote 13 pt 小型輔助文字
.caption 12 pt 圖片標題說明、細小印刷字
.caption2 11 pt 最小可讀文字

「預設大小」一欄顯示使用者「Large」動態字型設定(系統預設)下的尺寸。每個樣式都會在使用者調整偏好文字大小時等比放大或縮小;相對階層保持完整。

正確的採用做法是直接使用語意樣式:

VStack(alignment: .leading) {
    Text("Title").font(.title)
    Text("Subtitle").font(.subheadline).foregroundStyle(.secondary)
    Text("Body content here.").font(.body)
}

階層在每個動態字型設定下都保留,Apple HIG 慣例獲得尊重,字型也能在無需個別應用程式程式碼的情況下回應使用者的輔助使用偏好。

動態字型契約

動態字型是位於「設定 > 輔助使用 > 顯示與文字大小 > 較大文字」中由使用者控制的文字大小設定。其值以 DynamicTypeSize 流經環境,擁有十二個值,從 .xSmall.accessibility54:

  • 標準尺寸:.xSmall.small.medium.large(預設)、.xLarge.xxLarge.xxxLarge
  • 輔助使用尺寸:.accessibility1.accessibility2.accessibility3.accessibility4.accessibility5

使用語意文字樣式的應用程式可免費取得縮放。想要因應當前尺寸調整版面的應用程式則讀取環境:

struct AdaptiveLayout: View {
    @Environment(\.dynamicTypeSize) var dynamicTypeSize

    var body: some View {
        if dynamicTypeSize.isAccessibilitySize {
            VStack { content }    // stack vertically at accessibility sizes
        } else {
            HStack { content }    // horizontal at standard sizes
        }
    }
}

isAccessibilitySize 屬性涵蓋五種輔助使用尺寸(在這些尺寸下,文字大到水平版面常會破裂)。這個模式對任何依賴文字水平容納的版面都是正確選擇。

應用程式可用 .dynamicTypeSize(_:) 修飾符限制支援範圍:

ContentView()
    .dynamicTypeSize(.large ... .accessibility3)

此限制會為被修飾的子樹剪裁動態字型設定。當檢視的版面確實無法容納完整範圍時才使用;正確的預設是支援所有尺寸並讓版面自行調整。

自訂字型與動態字型

自訂字型(透過 Info.plistUIAppFonts 陣列隨應用程式發送的品牌字型)預設不會隨動態字型縮放。最簡單的修法是 Font.customrelativeTo: 參數:

// Doesn't scale with Dynamic Type
Text("Brand").font(.custom("MyBrandFont", size: 16))

// Scales with Dynamic Type relative to body
Text("Brand").font(.custom("MyBrandFont", size: 16, relativeTo: .body))

relativeTo: 參數告訴 SwiftUI 使用 body 樣式的縮放曲線來縮放自訂字型。在使用者「Large」設定下,字型大小是所請求的 16pt;在更大的設定下,SwiftUI 套用 body 樣式會用的相同倍數。

若需要更精緻的縮放(在不同尺寸下使用不同曲線、自訂光學處理),請直接使用 UIKit 的 UIFontMetrics。此模式較為冗長,但支援自訂字型常需要的逐尺寸調整。

字型何時失敗

值得一提的三種失敗模式:

到處都是固定大小的自訂字型。 最常見的 iOS 應用程式輔助使用失敗:用 Font.custom("BrandFont", size: 16)(無 relativeTo:)隨應用程式發送的品牌字型完全忽略動態字型。使用輔助使用文字尺寸的使用者看到品牌文字停留在 16pt,而系統文字縮放到 28pt 以上;視覺階層反轉。修法是在每處自訂字型使用都加上 relativeTo:,並在最大動態字型設定下透過 AccessibilityInspector 稽核。

為強調而硬編碼字重。 樣式為 .font(.body).fontWeight(.bold) 的副標題很脆弱:在輔助使用尺寸下,粗體 body 變得幾乎與已經很大的 body 無法區分。語意 .headline 樣式能在動態字型範圍中正確處理強調;請改用它,而非 body+bold。

在輔助使用尺寸下破裂的版面。.accessibility3 下溢位的「文字 + 圖示 + 文字」水平堆疊是動態字型暴露出來的版面臭蟲。修法是上述 dynamicTypeSize.isAccessibilitySize 的自適應版面模式;測試是在 QA 期間以最大動態字型執行應用程式,而不只是預設大小。

此模式對 iOS 26+ 應用程式的意義

三項要點。

  1. 使用語意文字樣式,而非手動調校的點數大小。 .body.headline.title2 等承載動態字型、光學尺寸與平台正確的階層。手動調校的 Font.system(size: 17) 擊敗每一項系統功能,並且當 Apple 調整預設斜率時會老化得很糟。

  2. 永遠在自訂字型上傳遞 relativeTo:Font.custom(_, size: _, relativeTo: .body) 隨應用程式發送的品牌字型參與動態字型。沒有它的品牌字型則是逐使用者的輔助使用退步,QA 只會在最大文字大小下發現。

  3. 在輔助使用動態字型尺寸下測試版面。 .accessibility3 設定大約是 Large 預設的 2 倍。在標準尺寸下看起來合宜的版面,在輔助使用尺寸下經常會破裂。修法是透過 dynamicTypeSize 環境進行版面層級的調整,而非透過 .dynamicTypeSize(...) 限制退出。

完整 Apple 生態系叢集:具型別的 App Intents;MCP 伺服器;路由問題;Foundation Models;執行期 vs 工具鏈 LLM 區隔;三個介面;單一真實來源模式;兩個 MCP 伺服器;Apple 開發用的 hooks;Live Activities;watchOS 執行期;SwiftUI 內部運作;RealityKit 的空間心智模型;SwiftData schema 紀律;Liquid Glass 模式;多平台發行;平台矩陣;Vision framework;Symbol Effects;Core ML 推論;Writing Tools API;Swift Testing;隱私清單;輔助使用作為平台;我拒絕寫的主題。中心位於 Apple 生態系列。如需更廣泛的 iOS 結合 AI 代理情境,請參閱 iOS Agent 開發指南

FAQ

SF Pro 與 SF Pro Display / SF Pro Text 有何不同?

從 iOS 9(SF 首次作為系統字型發行時)到 iOS 16,SF 以兩個獨立字型發行:SF Text 用於 20pt 以下的尺寸,SF Display 用於 20pt 及以上,各自針對其尺寸範圍有手動調校的字距與筆畫粗細。SF Pro Variable 將相同的 Text/Display 切分整合為連續的光學尺寸軸(opsz)。兩個字型不再分離;可變字型根據所請求的點數大小自動處理轉換。

如何在內文中取得等寬數字?

在 SwiftUI 中使用 .monospacedDigit():

Text("\(score)").font(.body).monospacedDigit()

此修飾符將 body 字型的比例數字換為等寬數字,同時保持文字其餘部分為比例式。請將其用於數字必須跨列對齊的任何 UI(計時器、計分板、餘額顯示)。

我應該為所有 UI 使用 SF Pro Rounded 嗎?

不應該。SF Pro Rounded 帶有一種語氣(友善、平易近人),適合某些情境,不適合其他。Watch 應用程式的複雜功能、iPhone 的電話號碼鍵盤,以及某些健康應用程式的介面會使用它。生產力應用程式、銀行應用程式或開發者工具通常不應使用。請刻意伸手取用 .rounded,而非作為預設。

iPhone 應用程式正確的動態字型範圍是什麼?

預設支援從 .xSmall.accessibility5 的所有尺寸。輔助使用尺寸(.accessibility1.accessibility5)是視力低下、行動困難或其他輔助使用需求的使用者使用 iPhone 的方式。透過 .dynamicTypeSize(...) 限制退出的應用程式辜負了那些使用者。正確的做法是版面調整(isAccessibilitySize 模式),而非退出尺寸範圍。

我可以隨應用程式發送自訂可變字型嗎?

可以。可變字型像任何其他自訂字型一樣發送(加入 Info.plistUIAppFonts,以 Font.custom 引用)。若要從 SwiftUI 處理可變軸,請透過 UIFontDescriptor.SymbolicTraits 使用 Font.custom 底層的 CTFont API,或為了完整的軸控制,降至 CTFontCreateCopyWithAttributes 搭配 kCTFontVariationAttribute。從 SwiftUI 到可變軸的橋接比系統字型冗長;對大多數應用程式而言,系統字型已涵蓋這些情境。

參考資料


  1. Apple Developer:Fonts。系統字型家族總覽,包含 SF Pro、SF Pro Rounded、SF Mono、SF Compact 與 New York。 

  2. Apple Developer:Meet the expanded San Francisco font family(WWDC 2022 場次 110381)。介紹 SF Pro Variable 的三軸設計(字重、寬度、光學尺寸)。 

  3. Apple Developer Documentation:Font.custom(_:size:relativeTo:)。讓字型加入動態字型縮放、相對於所選文字樣式的自訂字型初始化器。 

  4. Apple Developer Documentation:DynamicTypeSize。從 .xSmall.accessibility5 的十二值列舉,加上用於版面層級調整的 isAccessibilitySize 述詞。 

相關文章

Liquid Glass in SwiftUI: Three Patterns From Shipping Return on iOS 26

Apple's Liquid Glass is a one-line SwiftUI API. Three patterns from Return go beyond .glassEffect(): glass on text via C…

19 分鐘閱讀

Accessibility As Platform: Personal Voice, Live Speech, Eye Tracking, Music Haptics

Personal Voice, Live Speech, Eye Tracking, Music Haptics, Vocal Shortcuts: accessibility as platform features, not app r…

14 分鐘閱讀

The Design Engineer's Agent Stack

Design engineers need agent infrastructure that enforces visual consistency, typography discipline, color compliance, an…

14 分鐘閱讀