モバイル基盤グループのヴァンサン(@vincentisambart)です。
iOSの設定画面の右側は一定の幅を超えないように作られています。
iPadでは:
新iPad Pro 12.9"では:
iPadでTwitterのタイムラインのセルの中身も一定の幅を超えません。
このように、自分のアプリで広い画面でもコンテンツが広がりすぎないようにするためにはどうすればよいのでしょうか。AutoLayoutでいくつかの制約を使ってできるのですが、もっと簡単な方法はないのでしょうか。
iOS 9以上では、端末の種類を気にせず、複雑なAutoLayout制約を使わず、殆どのビューですぐ使える仕組みがあります。Appleのドキュメントで「readable content」や「readable width」と呼ばれているものです。以下日本語で「読みやすい幅」と呼ぶことにします。
注意点:下記の説明はAutoLayoutを使う前提で書かれています。AutoLayoutを使わない場合、親ビューのreadableContentGuide
のlayoutFrame
で読みやすい幅の明確な数字が取れるので、それを元にレイアウトを計算することになるのではないでしょうか。
(Twitterは読みやすい幅機能が使える前からこの表示でしたので、実際独自で同じことを実装しているようです。)
読みやすい幅とは
文章の表示領域の幅が広がりすぎると、少し読みづらく感じてしまいませんか。iOS 9以上では、readableContentGuideを使って幅が広がりすぎないようにできます。ビューのレイアウトの制約を定義する時、親ビューのleadingAnchor
/trailingAnchor
/leftAnchor
/rightAnchor
やlayoutMarginsGuide
の代わりにreadableContentGuide
を使います。(因みに読みやすい幅のためのガイドなので、readableContentGuide
の垂直方向のアンカーtopAnchor
、bottomAnchor
、centerYAnchor
、heightAnchor
が実質layoutMarginsGuide
のと同じです)
縦向きのiPhoneにしか表示されないコンテンツは読みやすい幅を使うメリットがあまりないのですが、iPadや横向きのiPhoneでも表示されるコンテンツは画面の全幅に渡ると少し読みづらくなるので読みやすい幅を適切に使えば良いではないでしょうか。
因みに「読みやすい」といっても、テキストの幅を合わせるだけでなく、セルに入っている画像やボタンの配置も読みやすい幅に合わせたほうが自然だと思います。
「読みやすい幅」は具体的にどういう幅なのでしょうか。全画面のroot view controllerのビューに子ビューを貼って、その子ビューの制約を親ビューのreadableContentGuide
に合わせて、子ビューの幅を測ってみました。
iPadでの表示:
端末 | 向き | 画面全幅③ | 読みやすい 最大幅④ | 余白 (③-④) | 左右マージン ((③-④)/2) |
---|---|---|---|---|---|
iPhone Xs Max iPhone Xr iPhone 8 Plus | 縦 | 414 pt | 374 pt | 40 pt | 20 pt |
iPhone Xs Max iPhone Xr | 横 | 896 pt | 672 pt | 224 pt | 112 pt |
iPhone 8 Plus | 横 | 736 pt | 696 pt | 40 pt | 20 pt |
iPhone Xs iPhone 8 | 縦 | 375 pt | 343 pt | 32 pt | 16 pt |
iPhone 8 | 横 | 667 pt | 627 pt | 40 pt | 20 pt |
iPhone Xs | 横 | 812 pt | 772 pt | 40 pt | 20 pt |
iPhone SE | 縦 | 320 pt | 288 pt | 32 pt | 16 pt |
iPhone SE | 横 | 568 pt | 528 pt | 40 pt | 20 pt |
iPad | 縦 | 768 pt | 672 pt | 96 pt | 48 pt |
iPad | 横 | 1024 pt | 672 pt | 352 pt | 176 pt |
iPad Pro 11" | 縦 | 834 pt | 672 pt | 162 pt | 81 pt |
iPad Pro 11" | 横 | 1194 pt | 672 pt | 522 pt | 261 pt |
上記のサイズは画面の全幅のを使う場合の数字ですが、readableContentGuide
が必ず自分のビューのlayoutMarginsGuide
に収まるように設計されているので、上記の幅が最大値です。親ビューのマージンがもっと大きかったり、親ビューがもっと小さかったりすると、読みやすい幅が小さくなります。
また、注意すべき点として、readableContentGuide
がlayoutMarginsGuide
の水平方向で中央になるように設計されているので、左右のマージンが違っていれば、readableContentGuide
がビューの中央になりません。ただ様々な設定によって(マージンが例えばsafe areaを含むように)マージンが変わるので、指定した左右のマージンが違ってもreadableContentGuide
がビューの中央になることもあります。
表の数字をまとめてみると、iPadでは、画面サイズや向きが何であろうと、幅が最大672 ptになるように計算されているようです(上記の表に載っていないiPad Pro 10.5"もiPad Pro 12.9"もそうです)。横向きのiPhone XsやiPhone 8 Plusでは、読みやすい幅がiPadより大きくなるのか少し気になりますが。
縦向きのiPhoneではreadableContentGuide
を使うと左右の余白が最低でも16 ptになるように見えます(因みにiOSで殆どのビューの標準のマージンが8 ptです)。ただし、色んなケースを試してみると、親ビューが全画面でない場合、左右の余白が12 ptになったりしますし、layoutMargins
やdirectionalLayoutMargins
で(またはInterface Builderで)親ビューのマージンを小さくすると、読みやすい幅の左右の余白がもっと小さくなることがあります。絶対なのはマージンに収まるように設計されているところだけのようですね。
上記の数字にはsafe areaが含まれていません。readableContentGuide
をそのまま使うとsafe area外になる可能性があります。本当に画面全体でreadableContentGuide
を使う場合、そのreadableContentGuide
を持っているビューのinsetsLayoutMarginsFromSafeArea
をtrue
にすれば良いかもしれません。insetsLayoutMarginsFromSafeArea
をtrue
にすると、マージンにsafe areaが含まれるようになり、readableContentGuide
がマージン内になるように設計されているので、readableContentGuide
もsafe area内になります。因みに、safe area関連の機能は全部iOS 11以上でしか使えません。
Interface Builder
コードでは、readableContentGuide
を使って制約を定義すれば良いのですが、ビューをInterface Builderで配置する場合、どうすれば良いのでしょうか。
やるべきことが2つあります:
読みやすい幅に合わせたいビューの親ビューのレイアウトの設定に「Follow Readable Width」にチェックを入れます。
- 読みやすい幅に合わせたいビューの配置に使われるsuperviewに対する左右の制約がマージンに対する制約である必要があります。
制約の詳細にある「First/Second Item」が「Superview.Leading」や「Superview.Trailing」ではなく、「Superview.Leading Margin」や「Superview.Trailing Margin」である必要があります。変えるには、その「First/Second Item」をクリックして、出てくるメニューに「Relative to margin」を選びましょう。
「First/Second Item」が「Safe Area」の場合、それをまず「Superview」に変えてから、「Relative to margin」を選びましょう。
ビューが必ずsafe area内に配置されるようにしたかったら、親ビューのレイアウト設定に「Safe Area Relative Margins」にチェックを入れてください。コード上ではinsetsLayoutMarginsFromSafeArea
と同じです。
Dynamic Type
読みやすい幅は便利そうではありますが、使う前に注意すべき点が1つあります:Dynamic Typeです。
Dynamic Typeというのはユーザーが使われるフォントのサイズを変えられる機能です。iOS全体の設定に一般→アクセシビリティ→さらに大きな文字(英語だとSettings→General→Accessibility→Larger Text)で変えられます。対応しているアプリではフォントサイズがその設定に合わせられます。
readableContentGuideのドキュメントを見ると、従われるルールが3つあります:
- The readable content guide never extends beyond the view’s layout margin guide.
- The readable content guide is vertically centered inside the layout margin guide.
- The readable content guide’s width is equal to or less than the readable width defined for the current dynamic text size.
1つめと2つめのルールは既に説明しましたが、3つめはまだでした。上記の表の数字はアクセシビリティ設定で標準のフォントサイズが選択されている場合の値です。もっと大きいフォントサイズが選ばれている場合、読みやすい幅が(マージン内で)広くなります。もっと小さいフォントサイズが選ばれている場合、読みやすい幅が狭くなります。
別の言い方をすると、iOSの「読みやすい幅」の制限が行のポイント数ではなく、その行に入れる文字数と言えるかもしれません。
Dynamic Type対応を既にしてあるアプリは幅が選ばれたフォントサイズに合わせられた方が綺麗に表示されますが、Dynamic Type対応をしていないアプリは大きいフォントサイズが選択されている場合表示が少し読みづらくなりそうです。
ユーザーの選んだフォントサイズカテゴリはUIApplication.shared.preferredContentSizeCategory
で取れます。カテゴリが多いので、どう変わるのかのスクリーンショットは標準サイズ(large
)、一番小さいサイズ(extraSmall
)、「さらに大きな文字」が有効になっていない場合の一番大きいサイズ(extraExtraExtraLarge
)、「さらに大きな文字」が有効になっている場合の一番大きいサイズ(accessibilityExtraExtraExtraLarge
)だけのにしました。
large
(標準設定)extraSmall
extraExtraExtraLarge
accessibilityExtraExtraExtraLarge
読みやすい幅のまとめ表
Dynamic Typeの設定によって読みやすい幅の数字をまとめてみました。上記のスクリーンショット同様4つのカテゴリだけに絞りました。
どう変わるのか見せるために明確な数字を載せていますが、その明確な数字をビューの配置のために使わないでおきましょう。今後iOS端末の画面サイズの種類がまた増えても不思議ではありませんし、今後iOSのメジャーアップデートで数字が少し変わる可能性もあります。ビューをpixel perfectで配置できていた時代はもう終わっています。
iPhone SE
preferred content size category | 向き | 画面全幅③ | 読みやすい 最大幅④ | 余白 (③-④) | 左右マージン ((③-④)/2) |
---|---|---|---|---|---|
large | 縦 | 320 pt | 288 pt | 32 pt | 16 pt |
large | 横 | 568 pt | 528 pt | 40 pt | 20 pt |
extraSmall | 縦 | 320 pt | 288 pt | 32 pt | 16 pt |
extraSmall | 横 | 568 pt | 528 pt | 40 pt | 20 pt |
extraExtraExtraLarge | 縦 | 320 pt | 288 pt | 32 pt | 16 pt |
extraExtraExtraLarge | 横 | 568 pt | 528 pt | 40 pt | 20 pt |
accessibilityExtraExtraExtraLarge | 縦 | 320 pt | 288 pt | 32 pt | 16 pt |
accessibilityExtraExtraExtraLarge | 横 | 568 pt | 528 pt | 40 pt | 20 pt |
フォントサイズが変わっても、読みやすい幅は変わらないようですね。
iPhone 8
preferred content size category | 向き | 画面全幅③ | 読みやすい 最大幅④ | 余白 (③-④) | 左右マージン ((③-④)/2) |
---|---|---|---|---|---|
large | 縦 | 375 pt | 343 pt | 32 pt | 16 pt |
large | 横 | 667 pt | 627 pt | 40 pt | 20 pt |
extraSmall | 縦 | 375 pt | 343 pt | 32 pt | 16 pt |
extraSmall | 横 | 667 pt | 560 pt | 107 pt | 53 pt |
extraExtraExtraLarge | 縦 | 375 pt | 343 pt | 32 pt | 16 pt |
extraExtraExtraLarge | 横 | 667 pt | 627 pt | 40 pt | 20 pt |
accessibilityExtraExtraExtraLarge | 縦 | 375 pt | 343 pt | 32 pt | 16 pt |
accessibilityExtraExtraExtraLarge | 横 | 667 pt | 627 pt | 40 pt | 20 pt |
縦向きの場合、フォントサイズが変わっても、読みやすい幅は変わらないようですが、横向きだと(少し余裕あるので)少し変わるようです。
iPhone 8 Plus
preferred content size category | 向き | 画面全幅③ | 読みやすい 最大幅④ | 余白 (③-④) | 左右マージン ((③-④)/2) |
---|---|---|---|---|---|
large | 縦 | 414 pt | 374 pt | 40 pt | 20 pt |
large | 横 | 736 pt | 696 pt | 40 pt | 20 pt |
extraSmall | 縦 | 414 pt | 374 pt | 40 pt | 20 pt |
extraSmall | 横 | 736 pt | 560 pt | 176 pt | 88 pt |
extraExtraExtraLarge | 縦 | 414 pt | 374 pt | 40 pt | 20 pt |
extraExtraExtraLarge | 横 | 736 pt | 696 pt | 40 pt | 20 pt |
accessibilityExtraExtraExtraLarge | 縦 | 414 pt | 374 pt | 40 pt | 20 pt |
accessibilityExtraExtraExtraLarge | 横 | 736 pt | 696 pt | 40 pt | 20 pt |
iPhone 8同様、縦向きの場合、フォントサイズが変わっても、読みやすい幅は変わらないようですが、横向きだと(少し余裕あるので)少し変わるようです。
iPhone Xs
preferred content size category | 向き | 画面全幅③ | 読みやすい 最大幅④ | 余白 (③-④) | 左右マージン ((③-④)/2) |
---|---|---|---|---|---|
large | 縦 | 375 pt | 343 pt | 32 pt | 16 pt |
large | 横 | 812 pt | 772 pt | 40 pt | 20 pt |
extraSmall | 縦 | 375 pt | 343 pt | 32 pt | 16 pt |
extraSmall | 横 | 812 pt | 560 pt | 252 pt | 126 pt |
extraExtraExtraLarge | 縦 | 375 pt | 343 pt | 32 pt | 16 pt |
extraExtraExtraLarge | 横 | 812 pt | 772 pt | 40 pt | 20 pt |
accessibilityExtraExtraExtraLarge | 縦 | 375 pt | 343 pt | 32 pt | 16 pt |
accessibilityExtraExtraExtraLarge | 横 | 812 pt | 772 pt | 40 pt | 20 pt |
iPhone 8同様、縦向きの場合、フォントサイズが変わっても、読みやすい幅は変わらないようですが、横向きだと(少し余裕あるので)少し変わるようです。
iPhone Xr、iPhone Xs Max
preferred content size category | 向き | 画面全幅③ | 読みやすい 最大幅④ | 余白 (③-④) | 左右マージン ((③-④)/2) |
---|---|---|---|---|---|
large | 縦 | 414 pt | 374 pt | 40 pt | 20 pt |
large | 横 | 896 pt | 672 pt | 224 pt | 112 pt |
extraSmall | 縦 | 414 pt | 374 pt | 40 pt | 20 pt |
extraSmall | 横 | 896 pt | 560 pt | 336 pt | 168 pt |
extraExtraExtraLarge | 縦 | 414 pt | 374 pt | 40 pt | 20 pt |
extraExtraExtraLarge | 横 | 896 pt | 856 pt | 40 pt | 20 pt |
accessibilityExtraExtraExtraLarge | 縦 | 414 pt | 374 pt | 40 pt | 20 pt |
accessibilityExtraExtraExtraLarge | 横 | 896 pt | 856 pt | 40 pt | 20 pt |
他のiPhone同様、縦向きの場合、フォントサイズが変わっても、読みやすい幅は変わらないようですが、横向きの場合、フォントサイズによって(もう少し余裕あるので)それなりに変わるようです。
iPad
preferred content size category | 向き | 画面全幅③ | 読みやすい 最大幅④ | 余白 (③-④) | 左右マージン ((③-④)/2) |
---|---|---|---|---|---|
large | 縦 | 768 pt | 672 pt | 96 pt | 48 pt |
large | 横 | 1024 pt | 672 pt | 352 pt | 176 pt |
extraSmall | 縦 | 768 pt | 560 pt | 208 pt | 104 pt |
extraSmall | 横 | 1024 pt | 560 pt | 464 pt | 232 pt |
extraExtraExtraLarge | 縦 | 768 pt | 728 pt | 40 pt | 20 pt |
extraExtraExtraLarge | 横 | 1024 pt | 896 pt | 128 pt | 64 pt |
accessibilityExtraExtraExtraLarge | 縦 | 768 pt | 728 pt | 40 pt | 20 pt |
accessibilityExtraExtraExtraLarge | 横 | 1024 pt | 984 pt | 40 pt | 20 pt |
小さめなフォントサイズの場合、縦向きと横向きでは幅が同じですが、大きめなフォントサイズの場合そういうわけでもありませんね。
iPad Pro 11"
preferred content size category | 向き | 画面全幅③ | 読みやすい 最大幅④ | 余白 (③-④) | 左右マージン ((③-④)/2) |
---|---|---|---|---|---|
large | 縦 | 834 pt | 672 pt | 162 pt | 81 pt |
large | 横 | 1194 pt | 672 pt | 522 pt | 261 pt |
extraSmall | 縦 | 834 pt | 560 pt | 274 pt | 137 pt |
extraSmall | 横 | 1194 pt | 560 pt | 634 pt | 317 pt |
extraExtraExtraLarge | 縦 | 834 pt | 794 pt | 40 pt | 20 pt |
extraExtraExtraLarge | 横 | 1194 pt | 896 pt | 298 pt | 149 pt |
accessibilityExtraExtraExtraLarge | 縦 | 834 pt | 794 pt | 40 pt | 20 pt |
accessibilityExtraExtraExtraLarge | 横 | 1194 pt | 1154 pt | 40 pt | 20 pt |
通常のiPad同様、小さめなフォントサイズの場合、縦と横では幅が同じですが、大きめなフォントサイズの場合そういうわけでもありませんね。
セル
一番上に載せたiOSの設定画面の例もTwitterの例もテーブルでした。Twitterの方は左右の余白でもセルがタッチに反応するので、読みやすい幅に合わせられたのはテーブル自体ではなく、セル内のコンテンツでしょう。
セルの場合、普通のビューと少し違うところあるので、それを見ましょう。
UITableView
UITableView
のセルの挙動は他のビューと同じです:
- コードでビューを配置する場合、readableContentGuide
を使えます。
- Interface Builderを使う場合、制約はマージンに対して定義し、親ビュー(おそらくcontent view)のレイアウト設定の「Follow Readable Width」にチェックを入れたら読みやすい幅に合わせられます。
でも、よりよい方法として、UITableView
にcellLayoutMarginsFollowReadableWidth
というプロパティを使う方法もあります。Interface Builderでは、UITableViewの「Follow Readable Width」にチェックを入れるのと同じです。
cellLayoutMarginsFollowReadableWidth
は自分が定義したカスタムセルだけではなく、テーブルの区切り線にもUIKitが提供しているカスタムでないCellStyleにも影響しています。
いくつかのセルCellStyleの一例、cellLayoutMarginsFollowReadableWidth
がfalse
の場合:
cellLayoutMarginsFollowReadableWidth
がtrue
の場合:
因みにcellLayoutMarginsFollowReadableWidth
がtrue
の場合、content viewのlayoutMarginsGuide
とreadableContentGuide
のどっちを使っても同じことになります。
UICollectionView
UICollectionView
に全幅に渡るセルがあれば、読みやすい幅を使いたくなることあるかもしれません。ただ、UICollectionView
にはcellLayoutMarginsFollowReadableWidth
のようなプロパティがありません。
コードではreadableContentGuide
は使えますが、Interface Builderでセル(実質セルのcontent view)のレイアウト設定の「Follow Readable Width」にチェックを入れても効果がありません(バグ)。でもワークアラウンドとしてcontent viewの中にビューを入れ、そのビューの「Follow Readable Width」にチェックを入れ、他のビューをその中に入れ、制約をそのビューのマージンに対して定義すれば動くようです。
まとめ
iOSでは、対応すべき画面サイズが少しずつ増えています。先日発表された新しいiPad Proも以前のと画面サイズが少し変わりました。事前に分かっている数少ない画面サイズを元にアプリの配置を決められる時代が終わっています。
その中でできるだけ多くの画面サイズに合わせて綺麗に配置できるツールの1つとして、読みやすい幅、または読みやすいコンテンツ、というのがあります。
読みやすいコンテンツを使うときはドキュメントにも書いてある3つのルールを覚えておきましょう:
readableContentGuide
が自分のビューのlayoutMarginsGuide
外に出ることはありません。readableContentGuide
が水平方向でlayoutMarginsGuide
の中央にあります。readableContentGuide
の幅が選ばれたDynamic Typeのフォントサイズ次第で決まる読みやすい幅以下です。