拡大表示 設定とはなにか
設定アプリの、「一般」 >「画面表示と明るさ」>「拡大表示」と選択を行い、「文字を拡大」を選択すると、iPhoneに表示されるすべてのテキストの文字が拡大されます。
アプリのフォントサイズを変更できるこの機能のことを、Dynamic Typeと言います。
本稿では、UILabelに、Dynamic Type対応を行う手順を紹介します。
UILabel Dynamic Type対応
以下二つの指定を行うことで実現できます。
- テキストスタイルを指定する
- Dinamic Type設定画面での変更内容通知を受け取る
TextStyleTextStylの指定
InterfaceBuilder上でも指定できますし、コードベースでも指定できます。
まずは視覚的に理解しやすい、InterfaceBuilderを用いた設定手法を紹介させてください。
InterfaceBuilderにてTextStyleを指定する
TextField >Font から設定を行うことができます。
画像中では Body
が指定されています。

(※) Interface Builderを使用する場合、使用しているフォントがカスタムフォントだとうまく動作しません。別途UIFontMetricsを用いて、コード上で指定を行う必要があります。
コード上で指定を行う
コードベースでText Styles を設定する場合、preferredFont(forTextStyle:)
メソッドを呼び出します。
[UILabelのプロパティ名].font = UIFont.preferredFont(forTextStyle: .body)

Dinamic Type設定 の変更通知を受け取る
ユーザーのDinamic Type設定変更の通知を受け取り、再描画を行います。
実現手法は3つあります。
- adjustsFontForContentSizeCategory をtrueに設定する
- didChangeNotificationを観察する
1. adjustsFontForContentSizeCategory をtrueに設定する
adjustsFontForContentSizeCategory
プロパティを true
に設定することで、ユーザーが提供するDynamic Type設定に基づいてテキストサイズを調整するようにテキストコントロールに指示できます。
最も一般的かつ、王道の方法です。
[UILabelのプロパティ名].adjustsFontForContentSizeCategory = true
2. didChangeNotificationを観察する
adjustsFontForContentSizeCategory が false
に設定されている場合、ユーザーが設定やコントロールセンターで行うテキストサイズの変更に応答しません。
UIContentSizeCategoryDidChange通知受信をトリガーに、Dinamic Type設定変更のイベント通知を実現できます。
// UIContentSizeCategoryDidChange の通知を監視する
NotificationCenter.default.addObserver(self, selector: Selector(("didChangePreferredContentSize:")), name: NSNotification.Name.UIContentSizeCategoryDidChange, object: nil)
この方法を使用する際は、UILabel の再読み込みも行わなければなりません。
Dinamic Type設定変更の通知は、即時にレイアウトへ反映させたいので、今回はlayoutIfNeeded
を使用します。
@objc func didChangePreferredContentSize(notification: NSNotification) {
[UILabelのプロパティ名].layoutIfNeeded()
}
【補足 1.】テキストスタイルとはなにか
テキストスタイル指定を行うと、該当テキストの使用目的をシステムに伝達できます。
使用目的は、見出し、本文、タイトル1 などの形式で表現され、これを受け取ったシステムは最適なサイズを動的に計算します。
テキストスタイルの使用を通じ、間接的にフォントサイズ指定も実現されます。
【補足 2.】setNeedsLayout() と layoutIfNeeded() の共通点と相違点
共通点
どちらのメソッドも、ビュー階層を再配置または更新するためのトリガー
相違点
- トリガーのタイミング:
setNeedsLayout()
:
再配置が必要であることをシステムに通知しますが、即座に再配置を行なわない。
代わりに、現在の実行ループが完了した後、自動的に再配置が行われる。
=> 非同期layoutIfNeeded()
:
呼び出された時点で即座にビュー階層の更新が行われる。
=> 同期的
- 戻り値の有無
setNeedsLayout()
:
戻り値なしlayoutIfNeeded()
:
ビューが再配置された場合にtrue
。再配置が行われなかった場合にfalse
が返却される。
参考
