Dynamic Typeは、ユーザーのシステム設定に応じてアプリのフォントサイズを自動的に変更するiOSの組み込み機能です。ユーザーは「設定」→「ディスプレイと明るさ」→「テキストサイズ」で主なテキストサイズを設定し、Dynamic Typeをサポートするすべてのアプリが追加の操作なしで適応します。Apple Human Interface Guidelines, 2024によると、Dynamic TypeのサポートはApp Storeでの公開に必須条件であり、iOSプラットフォームにおけるアクセシビリティの57fa本要素です。
ポイント
Dynamic TypeはiOS 7で導入された技術で、アプリのテキストをシステムのフォントサイズ設定に自動的に調整できます。ユーザーは一度にすべてのアプリでフォントを拡大または縮小できます — 各アプリ内の設定を個別に変更する必要はありません。
Apple WWDC 2023、「Make Your App Visually Accessible」によると、iOSユーザーの40%以上がフォントサイズをデフォルトから変更しています。65歳以上のユーザー群では、この数字は70%に達します。Dynamic TypeはApp Storeのアクセシビリティ認定に必須です。サポートの缺如は、レビュー中にアプリが拒否される理由です。
仕組み: アプリはUIFont(name:size:)の代わりにUIFont.preferredFont(forTextStyle:)を使用します。iOSは現在のシステム設定に応じてスケールされたフォントを自動的に返します。設定でサイズが変わると、iOSはUIContentSizeCategory.didChangeNotificationを発生します — アプリはレイアウトを再計算する必要があります。
Dynamic TypeはiOS 7でフラットデザインとともに登場しました。iOS 10ではUIFontMetricsを通じてカスタムフォントのサポートが追加されました。iOS 11ではAccessibilityXXXLまでのアクセシビリティレベルが導入されました。iOS 15ではSwiftUIにDynamic Typeが追加され、自動サポートが可能になりました。リリースごとにAppleは要件を強化しており、iOS 17以降では、Dynamic TypeをサポートしていないアプリはApp Store Connectへのアップロード時に警告が表示されます。
iOSは、デフォルト設定に">8つのテキストスタイルを提供しており、それぞれ事前に決められたサイズとウエイトを持ちます:
| スタイル | 定数 | サイズ (L) | 目的 |
|---|---|---|---|
| Large Title | .largeTitle | 34px | メイン画面タイトル |
| Title 1 | .title1 | 28px | セクション見出し |
| Title 2 | .title2 | 22px | サブヘッダー |
| Title 3 | .title3 | 20px | カードタイトル |
| Headline | .headline | 17px (太字) | 強調用太字 |
| Body | .body | 17px | 主なテキスト |
| Callout | .callout | 16px | 補助テキスト |
| Caption 1 | .caption1 | 12px | 画像のキャプション |
| Caption 2 | .caption2 | 11px | 小さなキャプション |
| Footnote | .footnote | 13px | 脚注、ノート |
正しいスタイルを使用することは、単なる「外観」ではありません。UIFont.TextStyle.bodyはAccessibilityXXXLモードで53pxに達することがあります。アプリがbodyに固定サイズ17pxのフォントを使用している場合、フォントサイズを拡大したユーザーにとってテキストが読みにくくなります。
Dynamic Typeはスケールだけではなく、視覚階層を保存します。Large TitleはいつもTitle 1より大きく、Title 1はBodyより大きくなります。スケーリング係数は異なります: 見出しは主なテキストよりもアグレッシブにスケールされ、アクセシビリティレベルでも階層が維持されます。
iOSは、テキストスケーリングの11レベルをサポートしており、2つのカテゴリに分けられます:
bodyスタイルのXSとAccessibilityXXXLの違いは14pxから53pxで、約4倍になります。17px用に設計されたレイアウトは53pxで完全に崩壊します: テキストが溢れ、ボタンが重なり、セルが衝突します。
コードで現在のカテゴリを確認:
let category = UIApplication.shared.preferredContentSizeCategory
// .extraSmall, .small, .medium, .large, .extraLarge ...
if category.isAccessibilityCategory {
// 代替レイアウトを有効化
}
テキストサイズカテゴリはtraitCollection.uiContentSizeCategoryからアクセスできます。システム設定が変更されると、iOSはすべてのUIViewでtraitCollectionDidChangeを呼びます。このメソッドで、フォントを更新し、レイアウトを再計算する必要があります。SwiftUIはこれを自動的に行いますが、UIKitでは手動でのサブスクリプションが必要です。
UICollectionViewにはUICollectionViewCompositionalLayoutを使用します — スクリーン幅とテキストサイズに応じて列数を自動調整します。アクセシビリティレベルでは、2列レイアウトから1列レイアウトに切り替えると、テキストが切り残されず、要素が重ならないようになります。条件付きレイアウトを使用します: 通常幅では2列、コンパクトまたはアクセシビリティでは1列です。
UIKitでの基本実装 — UIFont.preferredFont(forTextStyle:)。このメソッドは、現在のシステム設定に応じてスケールされたフォントを返します:
titleLabel.font = UIFont.preferredFont(forTextStyle: .headline)
bodyLabel.font = UIFont.preferredFont(forTextStyle: .body)
カスタムフォントにはUIFontMetricsを使用します:
let customFont = UIFont(name: "Montserrat-Regular", size: 16)!
titleLabel.font = UIFontMetrics(forTextStyle: .body)
.scaledFont(for: customFont)
// サイズ変更のトラッキング
NotificationCenter.default.addObserver(
self,
selector: #selector(preferredContentSizeChanged),
name: UIContentSizeCategory.didChangeNotification,
object: nil
)
Interface BuilderでUILabelを使用する場合は、フォントをテキストスタイルに設定し、「Automatically Adjusts Font」を有効にすればそれだけです。カスタムフォントの場合、Interface BuilderはUIFontMetricsをサポートしていないため、コードでのみ可能です。
UIFontMetricsはpreferredFontと同じスケーリング曲線を使用します。fromTextStyle: .body、.headlineなどを指定できます。それぞれのスタイルには独自のスケーリング係数があります。Bodyは中程度にスケールし、Large Titleはアグレッシブにスケールします。カスタムフォントを主なテキストに使用する場合は、.bodyを使用します。
SwiftUIでは、Dynamic Typeのサポートはデフォルトで組み込まれています。すべてのシステム変換子(.font(.body)、.font(.title))が自動スケールします。開発者がUIFont.preferredFontを呼び出す必要はありません。
例:
Text("主なテキスト")
.font(.body)
.lineLimit(nil)
.minimumScaleFactor(0.5)
カスタムスケールにはDynamicTypeSizeを使用します:
@Environment(\.dynamicTypeSize) var dynamicTypeSize
var body: some View {
Text("アダプティブテキスト")
.font(.body)
.padding(dynamicTypeSize <= .large ? 8 : 16)
}
SwiftUIはシステムのテキストサイズが変わると自動的にビューを更新します — 追加の通知サブスクリプションは不要です。
.font(.body)変換子はシステムフォントでのみ動作します。SwiftUIでカスタムフォントを使用する場合は、UIFontMetricsを内部で使用するFont.customを使用します。iOS 16以降では、SwiftUIは@EnvironmentでDynamicTypeSizeをサポートし、パディングとレイアウトを適応できるようになりました。
Auto LayoutはDynamic Typeサポートの前提条件です。固定幅と固定高さ(>=, <=)はアクセシビリティサイズで崩壊します。UILabelではintrinsicContentSizeを使用します — 現在のフォントサイズに応じて高さを自動計算します。
アダプティブレイアウトのルール:
長いテキスト(例: 記事、利用規約)の場合は、UILabelでminimumScaleFactorを設定します — Auto Layoutがコンテンツを収められない場合の最終手段としてテキストが小さくなります。0.5という値は、フォントが優先サイズの50%まで縮小できることを意味します。
サイズカテゴリに応じてパディングを調整する例:
let isAccessibility = traitCollection
.preferredContentSizeCategory.isAccessibilityCategory
stackView.spacing = isAccessibility ? 16 : 8
button.contentEdgeInsets = isAccessibility
? UIEdgeInsets(top: 16, left: 24, bottom: 16, right: 24)
: UIEdgeInsets(top: 8, left: 16, bottom: 8, right: 16)
テキストの切り残し — numberOfLines = 1で固定幅のUILabelはAccessibilityXLでテキストが切り残されます。解決策: numberOfLines = 0とtrailing制約。要素の重複 — 固定高さのボタンが互いに重なります。解決策: 自動スペーシングのUIStackView。画面外へのコンテンツの溢れ — アクセシビリティレベルではScrollViewが必須です。
Xcodeシミュレータでは、シミュレータのメニューからSettings → Accessibility → Display & Text Size → Larger Textと進んでテキストサイズを変更できます。すべての11つのレベルを切り替えて、テキストが切り残されていないか、ボタンがアクセス可能か、レイアウトが崩壊していないかを確認します。
自動テストには、XCUIApplicationのcontentSizeCategory設定を使用します:
func testAccessibilitySizes() {
let app = XCUIApplication()
app.launchArguments += [
"-UIPreferredContentSizeCategoryName",
"UICTContentSizeCategoryAccessibilityXXXL"
]
app.launch()
app.scrollViews.buttons["送信"].tap()
XCTAssertTrue(app.staticTexts["フォームが送信されました"]
.waitForExistence(timeout: 5))
}
異なるlaunchArgumentsを使用したXCUITestでは、CIですべてのスケーリングレベルをチェックできます。アクセシビリティカテゴリを別途テストするのを忘れないでください — レイアウトが崩壊するのはほとんどこれらのレベルです。Appleによると、Dynamic Typeのバグの70%はアクセシビリティレベルに特に見られ、開発者が標準サイズだけをテストすることが原因です。
Xcode Accessibility Inspectorは、選択した要素が使用しているフォント(固定またはスケーラブル)を表示します。要素がシミュレータのテキストサイズ変更に反応しない場合は、固定フォントが使用されています。UIFont.preferredFontまたはUIFontMetricsに置き換えてください。
よくある質問
システムのサイズ設定に応じてアプリ内で自動的にテキストをスケーリングする機能です。ユーザーが設定でサイズを変更すると、サポートするすべてのアプリが開発者の介入なしで適応します。
いいえ。カスタムフォントにはUIFontMetrics(scaledFont:)を使用します。このメソッドは、指定したテキストスタイルに相対して任意のフォントをスケールし、比例を保ちます。
11レベルです: 5つの標準(XS, S, M, L, XL)と6つのアクセシビリティレベル(AccessibilityXLからAccessibilityXXXXXLまで)。bodyフォントのサイズは14pxから53pxまで強います。
UIKitではUIContentSizeCategory.didChangeNotificationを使用します。SwiftUIでは@Environment(\.dynamicTypeSize)を使用します — システム設定が変わると自動的にビューが更新されます。
フォントを拡大しているユーザーには、切り残されたテキストや重なった要素、動かないボタンが表示されます。アクセシビリティ要件の違反で、App Storeのレビューで拒否される可能性があります。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。