SF Pro:可変軸、オプティカルサイジング、そしてDynamic Typeとの契約
SF Proは、2015年のiOS 9 / OS X El Capitan以来Appleのシステムフォントとなっており、3つの軸(ウェイト、幅、オプティカルサイズ)を持つ可変フォントです。サンセリフ(SF Pro)、ラウンド版(SF Pro Rounded)、等幅(SF Mono)、コンパクト版(SF Compact、watchOSで使用)、そしてセリフのコンパニオン(New York)を含む、設計されたファミリーとなっています1。このフォントはDynamic Typeとの契約を中心に設計されており、SwiftUIの11個のテキストスタイル(.largeTitle、.title、.body、.callout、.footnoteなど)はすべてデフォルトでSF Proを使用し、ユーザーが設定で優先テキストサイズを変更すると、すべて自動的にスケールします。
ほとんどのアプリは、これら11個のスタイルのうち1つか2つを使い、デフォルトの動作以上にDynamic Typeを意識せず、可変軸には触れません。その結果、機能はするものの、プラットフォームの語彙を話していないタイポグラフィになってしまいます。本記事では、システムフォントファミリー、可変軸、11個のセマンティックスタイル、Dynamic Typeとの契約、そしてカスタムフォントが参加するために明示的なスケーリング作業が必要となるケースを順に見ていきます。
TL;DR
- SF Pro Variableは3つの軸を公開しています:ウェイト(
wght)、幅(wdth)、オプティカルサイズ(opsz)2。オプティカルサイジングはポイントサイズに基づいて自動で行われ、ウェイトと幅はSwiftUIのFont.WeightとFont.Widthを通じて指定できます。 - システムファミリーには、SF Pro(デフォルト)、SF Pro Rounded(親しみやすいUI要素向け)、SF Mono(コード、技術的UI)、SF Compact(watchOS、狭いコンテキスト)、New York(編集向け読書のためのセリフコンパニオン)が含まれます。
- SwiftUIの11個のテキストスタイルは、Dynamic Typeを自動的にサポートします。
.bodyが安全なデフォルトであり、.largeTitleから.caption2までがプラットフォームの階層をカバーします。 - カスタムフォントはデフォルトではDynamic Typeでスケールしません。カスタムフォントをDynamic Typeのスケーリングに参加させるには、
Font.custom("Name", size: 16, relativeTo: .body)を使用してください3。 @Environment(\.dynamicTypeSize)により、ビューが現在のテキストサイズに合わせてレイアウトを適応できます。値の範囲は.xSmallから.accessibility5まで(合計12サイズ)です4。
システムフォントファミリー
Appleはシステムフォント体験に参加する5つのファミリーを提供しています。
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-heightを保ちつつ字送りを減らすため、SF Proでは窮屈に感じられるApple Watchの文字盤の寸法でも機能します。
watchOSアプリはシステムフォントを通じて自動的にSF Compactを使用します。iOSアプリでは通常、明示的にリクエストする必要はありません。
New York
編集向けの読書のためのコンパニオンとして設計されたセリフファミリーです。New Yorkはブックスでの長文テキスト、メモでの手書き風メモ、そしてSwiftUIでは.serifデザインを通じて登場します。
Text("Long-form essay").font(.system(.body, design: .serif))
セリフのコンパニオンはアプリUIではまれです。デフォルトとしてではなく、意図的に手を伸ばしてください(リーディングモード、引用文、記事本文など)。
3つの可変軸
SF Pro Variableは、組み合わさってあらゆるグリフを生み出す3つの軸をエンコードしています:
ウェイト(wght)
9つの名前付きウェイト:.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における4つの名前付き幅:.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サイズでのタイポグラフィは、見出しサイズでの同じフォントとは異なるプロポーションを持ち、それは設計上のものだということです。オプティカルサイジングを欠くカスタムフォントは1つのサイズではよく見えても、他のサイズでは間違って見えます。SF Proの自動処理はその落とし穴を完全に回避します。
11個のテキストスタイル
SwiftUIのFont.TextStyleは、すべてDynamic Typeに参加する11個のセマンティックスタイルを定義しています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」Dynamic Type設定(システムのデフォルト)でのサイズを示しています。各スタイルは、ユーザーが優先テキストサイズを調整するにつれて拡大または縮小し、相対的な階層は維持されます。
正しい採用方法は、セマンティックスタイルを直接使うことです:
VStack(alignment: .leading) {
Text("Title").font(.title)
Text("Subtitle").font(.subheadline).foregroundStyle(.secondary)
Text("Body content here.").font(.body)
}
階層はあらゆるDynamic Type設定で保たれ、Apple HIGの慣例が尊重され、タイポグラフィはアプリごとのコードなしにユーザーのアクセシビリティ設定に応答します。
Dynamic Typeとの契約
Dynamic Typeは、設定 > アクセシビリティ > 画面表示とテキストサイズ > さらに大きな文字、にあるユーザー制御のテキストサイズ設定です。値はDynamicTypeSizeとして環境を通じて流れ、.xSmallから.accessibility5まで12個の値を持ちます4:
- 標準サイズ:
.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プロパティは5つのアクセシビリティサイズをカバーします(テキストが大きすぎて横方向のレイアウトがしばしば破綻するサイズ)。このパターンは、テキストが横方向に収まることに依存するあらゆるレイアウトに適切です。
アプリは.dynamicTypeSize(_:)モディファイアでサポートする範囲を制限できます:
ContentView()
.dynamicTypeSize(.large ... .accessibility3)
この制約は、修飾されたサブツリーに対してDynamic Type設定をクリップします。ビューのレイアウトが本当に全範囲に対応できない場合に使用してください。正しいデフォルトは、すべてのサイズをサポートし、代わりにレイアウトを適応させることです。
カスタムフォントとDynamic Type
カスタムフォント(Info.plistのUIAppFonts配列を通じて出荷されるブランドフォント)は、デフォルトではDynamic Typeでスケールしません。最もシンプルな修正はFont.customのrelativeTo:パラメータです:
// 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:パラメータは、bodyスタイルのスケーリングカーブを使用してカスタムフォントをスケールするようSwiftUIに指示します。ユーザーの「Large」設定でのフォントサイズはリクエストされた16ptとなり、より大きな設定では、SwiftUIはbodyスタイルが使用するのと同じ倍率を適用します。
より洗練されたスケーリング(サイズごとに異なるカーブ、カスタムなオプティカル処理)には、UIKitのUIFontMetricsを直接使用してください。パターンはより冗長ですが、カスタムフォントがしばしば必要とするサイズごとの調整をサポートします。
タイポグラフィが失敗するとき
名前を付けるに値する3つの失敗モード:
至るところに固定サイズのカスタムフォント。 最も一般的なiOSアプリのアクセシビリティ失敗:Font.custom("BrandFont", size: 16)(relativeTo:なし)で出荷されたブランドフォントは、Dynamic Typeを完全に無視します。アクセシビリティテキストサイズを使うユーザーには、システムテキストが28pt以上にスケールする一方で、ブランドテキストは16ptで表示されます。視覚的な階層が逆転してしまいます。修正は、すべてのカスタムフォント使用に対するrelativeTo:であり、最大のDynamic Type設定でAccessibilityInspectorを通じて監査することです。
強調のためのハードコードされたウェイト。 .font(.body).fontWeight(.bold)でスタイル設定されたサブタイトルは脆弱です:アクセシビリティサイズでは、太字の本文は既に大きい本文とほとんど区別がつかなくなります。セマンティックな.headlineスタイルは、Dynamic Type範囲全体で強調を正しく処理します。body+boldの代わりにこちらを使ってください。
アクセシビリティサイズで破綻するレイアウト。 .accessibility3でオーバーフローするテキスト+アイコン+テキストの横方向スタックは、Dynamic Typeが露呈するレイアウトのバグです。修正は上記のdynamicTypeSize.isAccessibilitySize適応的レイアウトパターンです。テストは、デフォルトサイズだけでなく、QA中に最大のDynamic Typeでアプリを実行することです。
このパターンがiOS 26+アプリにとって意味すること
3つのテイクアウェイ。
-
手動調整のポイントサイズではなく、セマンティックテキストスタイルを使う。
.body、.headline、.title2などはDynamic Type、オプティカルサイジング、プラットフォーム正しい階層を備えています。手動調整したFont.system(size: 17)は、すべてのシステム機能を打ち破り、Appleがデフォルトのランプを調整したときにひどく古びます。 -
カスタムフォントには常に
relativeTo:を渡す。Font.custom(_, size: _, relativeTo: .body)で出荷されたブランドフォントはDynamic Typeに参加します。それなしで出荷されたブランドフォントは、QAが最大テキストサイズでのみ捕まえる、ユーザーごとのアクセシビリティ後退です。 -
アクセシビリティのDynamic Typeサイズでレイアウトをテストする。
.accessibility3設定はLargeデフォルトのおおよそ2倍です。標準サイズではよく見えるレイアウトが、アクセシビリティサイズではしばしば破綻します。修正は.dynamicTypeSize(...)制約を介してオプトアウトすることではなく、dynamicTypeSize環境を通じたレイアウトレベルの適応です。
Apple Ecosystemクラスター全体:型付きApp Intents;MCPサーバー;ルーティング問題;Foundation Models;ランタイム対ツーリングのLLM区別;3つの面;単一の真実の源パターン;2つのMCPサーバー;Apple開発のためのフック;Live Activities;watchOSランタイム;SwiftUIの内部;RealityKitの空間メンタルモデル;SwiftDataスキーマ規律;Liquid Glassパターン;マルチプラットフォーム出荷;プラットフォームマトリックス;Visionフレームワーク;Symbol Effects;Core ML推論;Writing Tools API;Swift Testing;プライバシーマニフェスト;プラットフォームとしてのアクセシビリティ;書くことを拒むこと。ハブはApple Ecosystem Seriesにあります。AIエージェントを伴うiOSのより広い文脈については、iOS Agent Developmentガイドをご覧ください。
FAQ
SF ProとSF Pro Display / SF Pro Textの違いは何ですか?
iOS 9(SFが最初にシステムフォントとして出荷されたとき)からiOS 16まで、SFは2つの別々のフォントとして出荷されていました:20pt未満のサイズ用のSF Textと20pt以上のサイズ用のSF Display、それぞれそのサイズ範囲のために手動調整された字間とストローク重量を備えていました。SF Pro Variableは、同じText/Displayの分割を連続的なオプティカルサイズ軸(opsz)に統合します。2つのフォントはもはや別々ではなく、可変フォントがリクエストされたポイントサイズに基づいて遷移を自動的に処理します。
本文テキストで等幅数字を取得するにはどうすればよいですか?
SwiftUIで.monospacedDigit()を使用してください:
Text("\(score)").font(.body).monospacedDigit()
このモディファイアは、テキストの残りの部分はプロポーショナルのまま、本文フォントのプロポーショナル数字を等幅数字に置き換えます。行を超えて数字を整列させる必要があるあらゆるUI(タイマー、スコアボード、残高表示など)に使ってください。
すべてのUIにSF Pro Roundedを使うべきですか?
いいえ。SF Pro Roundedは特定のコンテキストには合うが他には合わないトーン(親しみやすく、近づきやすい)を持っています。Watchアプリのコンプリケーション、iPhoneの電話番号パッド、特定のヘルスアプリの画面でこれを使用しています。生産性アプリ、銀行アプリ、開発者ツールでは一般的に使用すべきではありません。デフォルトとしてではなく、意図的に.roundedに手を伸ばしてください。
iPhoneアプリに適したDynamic Type範囲は何ですか?
.xSmallから.accessibility5まですべてのサイズをサポートすることをデフォルトとしてください。アクセシビリティサイズ(.accessibility1から.accessibility5)は、低視力、運動障害、その他のアクセシビリティニーズを持つユーザーがiPhoneを使用する方法です。.dynamicTypeSize(...)制約を介してオプトアウトするアプリは、それらのユーザーを失望させます。正しい動きは、サイズ範囲からオプトアウトするのではなく、レイアウトの適応(isAccessibilitySizeパターン)です。
アプリにカスタムの可変フォントを出荷できますか?
はい。可変フォントは他のカスタムフォントと同じように出荷します(Info.plistのUIAppFontsに追加し、Font.customで参照)。SwiftUIから可変軸を指定するには、Font.customの基礎となるCTFont APIをUIFontDescriptor.SymbolicTraitsを通じて使用するか、完全な軸制御のためにはkCTFontVariationAttributeを伴うCTFontCreateCopyWithAttributesに降りてください。SwiftUIから可変軸へのブリッジは、システムフォントよりも冗長です。ほとんどのアプリでは、システムフォントがケースをカバーします。
参考文献
-
Apple Developer:Fonts。SF Pro、SF Pro Rounded、SF Mono、SF Compact、New Yorkを含むシステムフォントファミリーの概要。 ↩
-
Apple Developer:Meet the expanded San Francisco font family(WWDC 2022セッション110381)。SF Pro Variableの3軸設計(ウェイト、幅、オプティカルサイズ)の紹介。 ↩
-
Apple Developerドキュメント:
Font.custom(_:size:relativeTo:)。選択したテキストスタイルに対して相対的にDynamic Typeスケーリングにフォントを参加させるカスタムフォントイニシャライザ。 ↩ -
Apple Developerドキュメント:
DynamicTypeSize。.xSmallから.accessibility5までの12値の列挙型と、レイアウトレベルの適応のためのisAccessibilitySize述語。 ↩↩↩