iOS Dynamic Type 対応方法について

iOS

Dynamic Type(ダイナミックタイプ)とは

ダイナミックタイプは iOS 11から利用可能な機能で、端末で表示するテキストのサイズを設定したサイズに自動的に変更する機能です。
ダイナミックタイプに対応したアプリは、ユーザが設定したフォントサイズで表示可能であるため、ダイナミックタイプでデフォルトのフォントサイズ以外を設定しているユーザに対してはユーザの好みに合わせたフォントサイズにすることが可能です。

設定方法

  1. 設定アプリを開く
  2. 「アクセシビリティ」をタップ
  3. 「画面表示とテキストサイズ」をタップ
  4. 「さらに大きな文字」をタップ
  5. 画面下部のスライダーでフォントサイズ指定
  6. より大きなフォントサイズを指定する場合は、「さらに大きな文字」という Switchをオンにしてスライダーで指定可能

ダイナミックタイプの設定画面

ダイナミックタイプで指定可能なフォントサイズ

ダイナミックタイプで指定可能なフォントサイズは決められており、詳細はHuman Interface GuidelineのTypographyを参照してください。
ユーザが設定するのは MediumやxLargeなどのUIContentSizeCategory を変更でき、デフォルトはLargeとなっている。
下記の StyleUIFont.TextStyleのことで、こちらは個々のアプリ側で指定可能。

ダイナミックタイプ Large指定の場合のフォントサイズ
「さらに大きな文字」をオンにした場合は、下記のようにさらにフォントサイズを大きくすることが可能。
UIContentSizeCategoryのAccessibility Sizes が対象となっている。

実装方法

実装方法の概観はこちらのApple公式記事がわかりやすいです。
Scaling Fonts Automatically
SwiftUI、UIKitどちらも実装はカンタンで、UILabelなどの文字を扱うUI要素に対して指定することで対応可能となります。詳細実装は後述します。

SwiftUI

import SwiftUI

struct ContentView: View {
    var body: some View {
        Text("Hello, world!")
            .font(.title)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
            .dynamicTypeSize(.medium)
    }
}
SwiftUIでは、iOS15+ からViewのinstance methodに dynamicTypeSize があるのでPreviewでの動作確認が簡易になりました。

UIKit

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var label: UILabel! {
        didSet {
            label.font = .preferredFont(forTextStyle: .title1)
            label.adjustsFontForContentSizeCategory = true
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()
    }
}
Storyboardでは、下記のように設定可能です。

Tips

ユーザが現在設定しているダイナミックタイプのサイズを取得したい

UIApplication preferredContentSizeCategory から取得可能です。

ダイナミックタイプでサポートするサイズを限定したい

SwiftUIの場合は dynamicTypeSize APIで下記のように範囲指定が可能です。
// SwiftUI
import SwiftUI

struct ContentView: View {
    var body: some View {
        Text("Hello, world!")
            .font(.title)
            .dynamicTypeSize(...DynamicTypeSize.large)
            // .dynamicTypeSize(DynamicTypeSize.medium...DynamicTypeSize.large) このような指定も可能
            .padding()
    }
}
UIKitの場合は、maximumContentSizeCategory , minimumContentSizeCategoryがiOS15+から可能なので、このAPIを指定することで限定的に指定することが可能です。
下記の場合は、mediumからxxxLargeまでのダイナミックタイプをサポートします。
    @IBOutlet weak var label: UILabel! {
        didSet {
            label.font = .preferredFont(forTextStyle: .title1)
            label.adjustsFontForContentSizeCategory = true
            label.maximumContentSizeCategory = .extraExtraExtraLarge
            label.minimumContentSizeCategory = .medium
        }
    }
deployment targetをiOS15以上にしている場合は、簡易に設定できますがiOS15未満もサポートしている場合は、前述の preferredContentSizeCategory から現在のフォントサイズを取得し、フォントサイズに応じてサポート範囲を変更することが必要となってきます。

アプリ全体に設定を反映したい

SwiftUIの場合は、親のViewに対してUIFont.TextStyleを指定することで反映可能です。
全体には、TextStyle.titleにし、局所的にTextStyleの変更は可能です。
// SwiftUI

import SwiftUI

@main
struct DynamicTypeAppApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
                .font(.title)
        }
    }
}

import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack {
            Text("Hello, world!")
                .font(.largeTitle)
                .padding()

            Text("test hoge hoge!")
                .font(.caption)
                .padding()
        }
    }
}

UIKitの場合は、UILabel.appearanceで設定することで全体に反映可能です。
@main
class AppDelegate: UIResponder, UIApplicationDelegate {

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.

        UILabel.appearance().font = .preferredFont(forTextStyle: .title1)
        UILabel.appearance().maximumContentSizeCategory = .extraExtraLarge
        UILabel.appearance().minimumContentSizeCategory = .small
        UILabel.appearance().adjustsFontForContentSizeCategory = true
        return true
    }

まとめ

ダイナミックタイプの設定方法および実装方法の詳細についてやTipsをまとめてみました。
ダイナミックタイプはダークモードなどの設定と同様にエンドユーザの好みに合わせてアプリが動的に変化するので、サポートされていると嬉しい機能です。
しかし、実装は簡易であるものの、アプリのデザイン上の都合やサポートコストを考慮すると導入は要検討となりそうな機能ではあると思いました。

参考リンク

最新情報をチェックしよう!