Dynamic Type: 主要概念、テキストスタイル、およびiOSでの仕組み

著者: IT Sectr 公開日: 2026-05-16 読了時間: 9 分

Dynamic Typeは、ユーザーのシステム設定に応じてアプリのフォントサイズを自動的に変更するiOSの組み込み機能です。ユーザーは「設定」→「ディスプレイと明るさ」→「テキストサイズ」で主なテキストサイズを設定し、Dynamic Typeをサポートするすべてのアプリが追加の操作なしで適応します。Apple Human Interface Guidelines, 2024によると、Dynamic TypeのサポートはApp Storeでの公開に必須条件であり、iOSプラットフォームにおけるアクセシビリティの57fa本要素です。

ポイント

  • Dynamic Type — システムのフォントサイズ設定に応じてテキストを自動スケーリングする iOSの仕組み
  • サポートはテキストスタイル(UIFont.TextStyle)— body, headline, caption, titleなどを通じて提供される
  • システムはXSmallからAccessibilityXXXLまで11レベルのスケーリングをサポート
  • カスタムフォントにはUIFontMetricsメソッドを使用し、任意のフォントをbodyスタイルに相対でスケール
  • レイアウトはアダプティブである必要がある — UIStackView、Auto Layout、ダイナミックセル高さが必須

iOSでのDynamic Typeとは

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の歴史

Dynamic TypeはiOS 7でフラットデザインとともに登場しました。iOS 10ではUIFontMetricsを通じてカスタムフォントのサポートが追加されました。iOS 11ではAccessibilityXXXLまでのアクセシビリティレベルが導入されました。iOS 15ではSwiftUIにDynamic Typeが追加され、自動サポートが可能になりました。リリースごとにAppleは要件を強化しており、iOS 17以降では、Dynamic TypeをサポートしていないアプリはApp Store Connectへのアップロード時に警告が表示されます。

UIFont.TextStyleテキストスタイル

iOSは、デフォルト設定に">8つのテキストスタイルを提供しており、それぞれ事前に決められたサイズとウエイトを持ちます:

スタイル定数サイズ (L)目的
Large Title.largeTitle34pxメイン画面タイトル
Title 1.title128pxセクション見出し
Title 2.title222pxサブヘッダー
Title 3.title320pxカードタイトル
Headline.headline17px (太字)強調用太字
Body.body17px主なテキスト
Callout.callout16px補助テキスト
Caption 1.caption112px画像のキャプション
Caption 2.caption211px小さなキャプション
Footnote.footnote13px脚注、ノート

正しいスタイルを使用することは、単なる「外観」ではありません。UIFont.TextStyle.bodyはAccessibilityXXXLモードで53pxに達することがあります。アプリがbodyに固定サイズ17pxのフォントを使用している場合、フォントサイズを拡大したユーザーにとってテキストが読みにくくなります。

スタイルによる視覚階層

Dynamic Typeはスケールだけではなく、視覚階層を保存します。Large TitleはいつもTitle 1より大きく、Title 1はBodyより大きくなります。スケーリング係数は異なります: 見出しは主なテキストよりもアグレッシブにスケールされ、アクセシビリティレベルでも階層が維持されます。

スケーリングレベル

iOSは、テキストスケーリングの11レベルをサポートしており、2つのカテゴリに分けられます:

  • 標準サイズ (5レベル): XS, S, M, L (デフォルト), XL
  • アクセシビリティサイズ (6レベル): accessibilityXL, accessibilityXXL, accessibilityXXXL, accessibilityXXXXL, accessibilityXXXXXL, accessibilityXXXXXXL

bodyスタイルのXSとAccessibilityXXXLの違いは14pxから53pxで、約4倍になります。17px用に設計されたレイアウトは53pxで完全に崩壊します: テキストが溢れ、ボタンが重なり、セルが衝突します。

コードで現在のカテゴリを確認:

swift
let category = UIApplication.shared.preferredContentSizeCategory
// .extraSmall, .small, .medium, .large, .extraLarge ...
if category.isAccessibilityCategory {
    // 代替レイアウトを有効化
}

traitCollectionとテキストサイズ

テキストサイズカテゴリはtraitCollection.uiContentSizeCategoryからアクセスできます。システム設定が変更されると、iOSはすべてのUIViewでtraitCollectionDidChangeを呼びます。このメソッドで、フォントを更新し、レイアウトを再計算する必要があります。SwiftUIはこれを自動的に行いますが、UIKitでは手動でのサブスクリプションが必要です。

UICollectionViewにはUICollectionViewCompositionalLayoutを使用します — スクリーン幅とテキストサイズに応じて列数を自動調整します。アクセシビリティレベルでは、2列レイアウトから1列レイアウトに切り替えると、テキストが切り残されず、要素が重ならないようになります。条件付きレイアウトを使用します: 通常幅では2列、コンパクトまたはアクセシビリティでは1列です。

UIKitでの実装

UIKitでの基本実装 — UIFont.preferredFont(forTextStyle:)。このメソッドは、現在のシステム設定に応じてスケールされたフォントを返します:

swift
titleLabel.font = UIFont.preferredFont(forTextStyle: .headline)
bodyLabel.font = UIFont.preferredFont(forTextStyle: .body)

カスタムフォントにはUIFontMetricsを使用します:

swift
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とスケーリング曲線

UIFontMetricsはpreferredFontと同じスケーリング曲線を使用します。fromTextStyle: .body、.headlineなどを指定できます。それぞれのスタイルには独自のスケーリング係数があります。Bodyは中程度にスケールし、Large Titleはアグレッシブにスケールします。カスタムフォントを主なテキストに使用する場合は、.bodyを使用します。

SwiftUIでのDynamic Type

SwiftUIでは、Dynamic Typeのサポートはデフォルトで組み込まれています。すべてのシステム変換子(.font(.body)、.font(.title))が自動スケールします。開発者がUIFont.preferredFontを呼び出す必要はありません。

例:

swift
Text("主なテキスト")
    .font(.body)
    .lineLimit(nil)
    .minimumScaleFactor(0.5)

カスタムスケールにはDynamicTypeSizeを使用します:

swift
@Environment(\.dynamicTypeSize) var dynamicTypeSize

var body: some View {
    Text("アダプティブテキスト")
        .font(.body)
        .padding(dynamicTypeSize <= .large ? 8 : 16)
}

SwiftUIはシステムのテキストサイズが変わると自動的にビューを更新します — 追加の通知サブスクリプションは不要です。

SwiftUIでのDynamic Typeの制限

.font(.body)変換子はシステムフォントでのみ動作します。SwiftUIでカスタムフォントを使用する場合は、UIFontMetricsを内部で使用するFont.customを使用します。iOS 16以降では、SwiftUIは@EnvironmentでDynamicTypeSizeをサポートし、パディングとレイアウトを適応できるようになりました。

大きなフォントへのレイアウト適応

Auto LayoutはDynamic Typeサポートの前提条件です。固定幅と固定高さ(>=, <=)はアクセシビリティサイズで崩壊します。UILabelではintrinsicContentSizeを使用します — 現在のフォントサイズに応じて高さを自動計算します。

アダプティブレイアウトのルール:

  • distribution = fill、spacing >= 8のUIStackView — 要素が次の行に振り替えられる
  • automaticDimensionのUITableViewCell — セルの高さがコンテンツに合わせて調整される
  • UILabelのnumberOfLines = 0 — テキストが切り残されずに折り返される
  • edgeInsets付きボタン — traitCollection.preferredContentSizeCategoryを使用してアクセシビリティレベルでパディングを追加

長いテキスト(例: 記事、利用規約)の場合は、UILabelでminimumScaleFactorを設定します — Auto Layoutがコンテンツを収められない場合の最終手段としてテキストが小さくなります。0.5という値は、フォントが優先サイズの50%まで縮小できることを意味します。

サイズカテゴリに応じてパディングを調整する例:

swift
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が必須です。

Dynamic Typeのテスト

Xcodeシミュレータでは、シミュレータのメニューからSettings → Accessibility → Display & Text Size → Larger Textと進んでテキストサイズを変更できます。すべての11つのレベルを切り替えて、テキストが切り残されていないか、ボタンがアクセス可能か、レイアウトが崩壊していないかを確認します。

自動テストには、XCUIApplicationのcontentSizeCategory設定を使用します:

swift
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%はアクセシビリティレベルに特に見られ、開発者が標準サイズだけをテストすることが原因です。

Accessibility Inspector: Dynamic Typeのチェック

Xcode Accessibility Inspectorは、選択した要素が使用しているフォント(固定またはスケーラブル)を表示します。要素がシミュレータのテキストサイズ変更に反応しない場合は、固定フォントが使用されています。UIFont.preferredFontまたはUIFontMetricsに置き換えてください。

よくある質問

簡単に言うとiOSのDynamic Typeとは何ですか?

システムのサイズ設定に応じてアプリ内で自動的にテキストをスケーリングする機能です。ユーザーが設定でサイズを変更すると、サポートするすべてのアプリが開発者の介入なしで適応します。

Dynamic Typeはシステムフォントでのみ動作しますか?

いいえ。カスタムフォントにはUIFontMetrics(scaledFont:)を使用します。このメソッドは、指定したテキストスタイルに相対して任意のフォントをスケールし、比例を保ちます。

iOSはいくつのスケーリングレベルをサポートしていますか?

11レベルです: 5つの標準(XS, S, M, L, XL)と6つのアクセシビリティレベル(AccessibilityXLからAccessibilityXXXXXLまで)。bodyフォントのサイズは14pxから53pxまで強います。

テキストサイズの変更をサブスクライブするには?

UIKitではUIContentSizeCategory.didChangeNotificationを使用します。SwiftUIでは@Environment(\.dynamicTypeSize)を使用します — システム設定が変わると自動的にビューが更新されます。

Dynamic Typeをサポートしないとどうなりますか?

フォントを拡大しているユーザーには、切り残されたテキストや重なった要素、動かないボタンが表示されます。アクセシビリティ要件の違反で、App Storeのレビューで拒否される可能性があります。

まとめ

  • Dynamic Type — システム設定に応じてテキストをスケールするiOSの組み込み機構; App Storeに必須
  • システムフォントにはUIFont.preferredFont(forTextStyle:)を、カスタムフォントにはUIFontMetricsを使用
  • iOSは11レベルのスケーリングをサポート — XSからAccessibilityXXXXXLまで(bodyは14–53px)
  • SwiftUIではDynamic Typeが.font(.body)と@Environment(\.dynamicTypeSize)で自動的に動作
  • レイアウトはアダプティブである必要: UIStackView、テーブルでのautomaticDimension、numberOfLines = 0
  • 全11レベルをテストし、尤もアクセシビリティカテゴリをチェック — 70%のレイアウトがこれらで崩壊する
  • UIKitアプリではUIContentSizeCategory.didChangeNotificationへのサブスクライブが必須

ターンキー方式のモバイルアプリケーションを開発します

IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。

プロジェクトについて相談

こちらもお読みください