iOS 11 の Safe Area は Auto Layout だけでなくコードベースでも取れる

  • 124
    Like
  • 4
    Comment

iOS 11 では Safe Area という概念を導入しました。そして本日のイベントをご覧になられた方、もしくは iPhone X を少しでも掘ってみた方ならわかると思いますが、この Safe Area の概念はレイアウトする時、特に iPhone X ではものすごい重要になってきます。

具体的に iPhone X 向けのレイアウト最適化の話については是非とも公式の Building Apps for iPhone XDesigning for iPhone X をご覧いただければと思いますが、ここではとりあえず筆者みたいな Auto Layout 死んでも使わない人()向けに「Safe Area」をコードで取る方法を簡単に説明したいと思います。(ちなみに Auto Layout でしたら safeAreaLayoutGuide を使うことができます)

iOS 11 からは UIView では safeAreaInsets という UIEdgeInsets 型のプロパティーが追加されました。公式の説明によりますとこれは get-only のプロパティーで、ビューヒエラルキーに依存しているとのことです。なので最終的には Status Bar や TabBar の他に、ビューヒエラルキーに入っている UIViewControlleradditionalSafeAreaInsets にも依存しています。

例えばルート 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.y10 なら、ChildView の safeAreaInsets.top44 - 10 = 34 になります。また、さらにこの ChildView に追加した子ビュー、つまり ParentView から見た GrandChildView でも、ビューヒエラルキーに則って ParentView から見た領域で safeAreaInsets を適切に読み取れます。例えば GrandChildView 自身の frame.origin.y10 なら、これは ChildView から見た 10 なので、さらに ChildView の 10 を合計した 20 こそが ParentView から見た frame.origin.y になります。ですので GrandChildView の中に残っている safeAreaInsets.top44 - 20 = 24 にならなければなりません。そして実際 GrandChildView の safeAreaInsets.top を確認してみれば確かにこれが 24 になります。

ただし、注意点としては、まだビューヒエラルキーに追加されていない場合(つまり superviewnil か、superview を遡ると最終的に nil になる場合)、safeAreaInsets.zero になります。

ちなみに、GitHub に簡単なサンプルプロジェクトを上げました。iPhone X のシミュレーターで起動すると下記のようなスクリーンショットになります。この時裏から青、赤、緑のビューのそれぞれの safeAreaInsets をコンソールで確認できます(筆者の環境の出力も下記に置いておきます)

スクリーンショット 2017-09-13 15.35.27.png

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 もこの機能には対応する予定です。