Edited at

iOSのDynamic Typeについて


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

Dynamic TypeはiOS上でデフォルトの文字の大きさを変更する機能です。対応しているアプリの文字サイズを一度に全部変えられます。

大きさだけでなくフォントの種類やスタイルも決められているので、Apple推奨の見た目を簡単に得られますが、開発者にとってはかなり厄介な機能です。テストの手間を増やすだけでなく、画面設計にも影響を及ぼします。

本文章は、Dynamic Typeに対する備忘録です。

対象OS:iOS9/iOS10/iOS11

対象Xcode:9.4


Dynamic Typeを使うためのユーザー操作

iOS9/10/11共通で、設定に2通り用意されています。


  • [設定] -> [画面表示と明るさ] -> [文字サイズを変更]

  • [設定] -> [一般] -> [アクセシビリティ] -> [さらに大きな文字]

後者では「さらに大きな文字」のスイッチをオンにすることで、文字サイズの選択肢を増やすことができます。


フォントの指定方法


インターフェースビルダーで指定

フォントを選択する際、Text Stylesにカテゴライズされているものを選びます。

XCodeのフォント選択の図


コードでのフォント指定

UIFontクラスのクラスメソッドpreferredFont()に"スタイル"を指定することで、設定に応じたフォントオブジェクトを得られます。

let font = UIFont.preferredFont(forTextStyle: UIFontTextStyle(rawValue: style))


動的な設定変更に追従するために

ダイナミックタイプは設定からいつでもサイズが変えられます。

この変更に動的に追従するためには、もう一手間が必要です。


iOS10以降、UILabel、UITextField、UITextViewは自動的に更新される

インターフェースビルダーで、Dynamic Typeの[Automatically Adjust Font]をチェックすると、表示される文字が自動的に更新されるようになります。

xcodeSelectDynamicType_small.png

コードでは、UIContentSizeCategoryAdjustingプロトコルのadjustsFontForContentSizeCategorytrueにすることで同様に自動的に処理されるようになります。

hogeLabel.adjustsFontForContentSizeCategory = true

UIContentSizeCategoryAdjustingプロトコルに対応しているのは、UILabelUITextFieldUITextViewだけです。

この指定によるフォントサイズ変更は、viewWillLayoutSubviews()viewDidLayoutSubviews()の間でされるようです。

UIContentSizeCategoryAdjustingプロトコルに対応していないUISegmentedControlなどは、次の通知によって対応することになります。


iOS9までは通知を受けて自分で反映する必要がある

UIContentSizeCategory.didChangeNotification通知(swift 4.1まではNSNotification.Name.UIContentSizeCategoryDidChange通知でした)によって、Dynamic Typeの動的な変更をキャッチすることができます。これをUIに反映することで、動的なサイズ変更に追従することができます。

var anObserver:NSObjectProtocol?

override func viewWillAppear(_ animated: Bool) {
anObserver = NotificationCenter.default.addObserver(forName: UIContentSizeCategory.didChangeNotification /*NSNotification.Name.UIContentSizeCategoryDidChange*/, object: nil, queue: OperationQueue.main, using: updateFont)
}
override func viewWillDisappear(_ animated: Bool) {
if anObserver != nil {
NotificationCenter.default.removeObserver(anObserver!)
anObserver = nil
}
}
func updateFont(_ notification:NSNotification) {
// UIFont.preferredFont() を使って全部再設定する。
hoge1.font = UIFont.preferredFont(forTextStyle: .body)
...
}

※2018/7/20:上記コードにobserverの解除ができてないバグがあり、修正しました。なお、iOS9以降はremoveObserverが不要、とAppleが言っていますが、それはオブジェクトが解放される場合の話のようです。UITabBarControllerを使って各コントローラーが解放されない場合、表示する度にobserverが追加されて一度の表示で何回も通知を受け取る羽目になります。

*2019/3/6:恥ずかしいミスを修正(obserber -> observer)


通知での設定が優先される

通知の呼び出しは設定からアプリケーションに戻ったところで発生します。その後で、viewWillLayoutSubviews()viewDidLayoutSubviews()が呼び出されます。しかし、通知を受け取ってフォントサイズを変更した場合、その後、adjustsFontForContentSizeCategory = trueによるフォントサイズの自動変更が更に発生して設定を上書きすることはありません。通知による変更は、adjustsFontForContentSizeCategory = trueよりも優先されるようです。

このため、表示崩れを防ぐために、通知の中で標準よりも小さい文字を指定して逃げるような使い方も可能ですし、インターフェースビルダーでのDynamic Typeのチェックも神経質にならずに使うことができます。


iOS9.3シミュレーターのバグ(?)

なお、XCode9.4に付属のiOS9.3のシミュレーターには、動的なサイズ変更が正しく反映されない不具合があるようです。変更は反映されず、通知も発行されません。 文字サイズの設定を変更した後、Cmd+WでシミュレーターのiPhoneを一旦終了すると、次の起動では反映されるようです。

iOS10以降のシミュレーターでは動的な文字サイズの変更をテストできます。

私はこれに気づかず何日か無駄にしてしまいました


設定されるフォントサイズ


iOS9/10の場合

設定のフォントサイズを変更した場合の各pointSizeを表にまとめます。

1から7までが通常指定できる大きさで、デフォルトは4です。

8以降は「さらに大きな文字」スイッチをオンにすることで選べるようになります。

スタイル
1
2
3
4
5
6
7
8
9
10
11
12

.body
14
15
16
17
19
21
23
28
33
40
47
53

.callout
13
14
15
16
18
20
22
22
22
22
22
22

.caption1
11
11
11
12
14
16
18
18
18
18
18
18

.caption2
11
11
11
11
13
15
17
17
17
17
17
17

.footnote
12
12
12
13
15
17
19
19
19
19
19
19

.headline
14
15
16
17
19
21
23
23
23
23
23
23

.subheadline
12
13
14
15
17
19
21
21
21
21
21
21

.title1
25
26
27
28
30
32
34
34
34
34
34
34

.title2
19
20
21
22
24
26
28
28
28
28
28
28

.title3
17
18
19
20
22
24
26
26
26
26
26
26

iOS10までは、さらに大きな文字で大きくなるのは.bodyだけでした。


iOS11の文字サイズ

iOS11ではスタイルが一つ増えただけでなく、個々の文字サイズも変わっています。(太字はiOS10までに対する変更点)

iPhone5/SE系:

スタイル
1
2
3
4
5
6
7
8
9
10
11
12

.body
14
15
16
17
19
21
23
28
33
40
47
53

.callout
13
14
15
16
18
20
22
26
32
38
44
51

.caption1
11
11
11
12
14
16
18
22
26
32
37
43

.caption2
11
11
11
11
13
15
17
20
24
29
34
40

.footnote
12
12
12
13
15
17
19
23
27
33
38
44

.headline
14
15
16
17
19
21
23
28
33
40
47
53

.subheadline
12
13
14
15
17
19
21
25
30
36
42
49

.title1
25
26
26
26
27
28
30
36
41
46
51
56

.title2
19
20
20
20
21
22
24
29
35
41
47
54

.title3
19
20
20
20
21
22
24
29
35
41
47
54

.largeTitle
31
32
32
32
33
34
35
42
46
50
54
58

「さらに大きな文字」をオンにしたときの文字サイズが.body以外も大きくなっています。

(title2とtitle3が同じなのは奇妙ですが、実際そうなっています。)

実際のフォントで図にしてみるとこうなります。

左のタイトルはデフォルト設定での各スタイルの文字で、右側の数字が個々の設定に応じた実際の大きさです。

capture.png

これだけ大きさが違うと簡単には対応できないですよね・・・

追記:2018/7/24 iOS11では、デバイス毎に異なるサイズが使われていることがわかったので以下を追加します。

iPhone6系, 6Plus系, X:

スタイル
1
2
3
4
5
6
7
8
9
10
11
12

.body
14
15
16
17
19
21
23
28
33
40
47
53

.callout
13
14
15
16
18
20
22
26
32
38
44
51

.caption1
11
11
11
12
14
16
18
22
26
32
37
43

.caption2
11
11
11
11
13
15
17
20
24
29
34
40

.footnote
12
12
12
13
15
17
19
23
27
33
38
44

.headline
14
15
16
17
19
21
23
28
33
40
47
53

.subheadline
12
13
14
15
17
19
21
25
30
36
42
49

.title1
25
26
27
28
30
32
34
38
43
48
53
58

.title2
19
20
21
22
24
26
28
34
39
44
50
56

.title3
17
18
19
20
22
24
26
31
37
43
49
55

.largeTitle
31
32
33
34
36
38
40
44
48
52
56
60

こちらも図にしてみました。

iOS11FontSizesOnX.png


終わりに

Dynamic TypeはiOS7で導入され、iOS11でさらに大きな文字に対応するように拡張され、iOS全体で対応が進みました。

Appleはもともとアクセシビリティに高い関心を寄せてきた会社ですが、社会的影響力が大きくなって、ますます熱意を注いでいるようです。特にiOS11では非常に大きな文字を指定できるようになりました。これは、Appleの弱視の人々に対する企業姿勢を表しているように思います。

残念ながら対応しているアプリは多くないようですが、こういうところにこそ、企業の姿勢が表れるように思います。特に小さい文字を見にくいと感じる人々にとっては、なくてはならない機能であり、高齢化の進んだ日本では多くのユーザーに影響を及ぼすようにも思います。

対応アプリが増えて、より多くの人にやさしい社会に近づくことを願います。