iOS 11 では Safe Area という概念を導入しました。そして本日のイベントをご覧になられた方、もしくは iPhone X を少しでも掘ってみた方ならわかると思いますが、この Safe Area の概念はレイアウトする時、特に iPhone X ではものすごい重要になってきます。
具体的に iPhone X 向けのレイアウト最適化の話については是非とも公式の Building Apps for iPhone X と Designing for iPhone X をご覧いただければと思いますが、ここではとりあえず筆者みたいな Auto Layout 死んでも使わない人()向けに「Safe Area」をコードで取る方法を簡単に説明したいと思います。(ちなみに Auto Layout でしたら safeAreaLayoutGuide
を使うことができます)
iOS 11 からは UIView
では safeAreaInsets
という UIEdgeInsets
型のプロパティーが追加されました。公式の説明によりますとこれは get-only のプロパティーで、ビューヒエラルキーに依存しているとのことです。なので最終的には Status Bar や TabBar の他に、ビューヒエラルキーに入っている UIViewController
の additionalSafeAreaInsets
にも依存しています。
例えばルート ViewController が保持している view
ParentView は、iPhone X の縦画面なら上に 44
、下に 34
ピクセルの Safe Area がこの safeAreaInsets
プロパティーで読み取れます(UIEdgeInsets(top: 44.0, left: 0.0, bottom: 34.0, right: 0.0)
)。そしてこの ParentView だけでなく、ParentView に追加した子ビュー ChildView も、ChildView 自分自身の frame
に応じて、適切な safeAreaInsets
が読み取れます。例えば ChildView の frame.origin.y
が 10
なら、ChildView の safeAreaInsets.top
は 44 - 10 = 34
になります。また、さらにこの ChildView に追加した子ビュー、つまり ParentView から見た GrandChildView でも、ビューヒエラルキーに則って ParentView から見た領域で safeAreaInsets
を適切に読み取れます。例えば GrandChildView 自身の frame.origin.y
が 10
なら、これは ChildView から見た 10
なので、さらに ChildView の 10
を合計した 20
こそが ParentView から見た frame.origin.y
になります。ですので GrandChildView の中に残っている safeAreaInsets.top
は 44 - 20 = 24
にならなければなりません。そして実際 GrandChildView の safeAreaInsets.top
を確認してみれば確かにこれが 24
になります。
ただし、注意点としては、まだビューヒエラルキーに追加されていない場合(つまり superview
が nil
か、superview
を遡ると最終的に nil
になる場合)、safeAreaInsets
は .zero
になります。
ちなみに、GitHub に簡単なサンプルプロジェクトを上げました。iPhone X のシミュレーターで起動すると下記のようなスクリーンショットになります。この時裏から青、赤、緑のビューのそれぞれの safeAreaInsets
をコンソールで確認できます(筆者の環境の出力も下記に置いておきます)
self: UIEdgeInsets(top: 44.0, left: 0.0, bottom: 34.0, right: 0.0)
sub: UIEdgeInsets(top: 34.0, left: 0.0, bottom: 24.0, right: 0.0)
grand: UIEdgeInsets(top: 24.0, left: 0.0, bottom: 14.0, right: 0.0)
(上記の出力について、self
は一番裏にある青のビュー、sub
は一個上にある赤のビュー、そして grand
は一番上にある緑のビューを指します)
余談ですが、当然 NotAutoLayout もこの機能には対応する予定です。