iOS
Swift
iPhoneX
iOSDay 23

iPhone X対応 ~Safe Areaの外側~

今年、iOSエンジニアを苦しめたであろうiPhone X対応。私も苦しめられた者の1人なのでその知見を投下しておこうと思います。この内容は2017年 AKIBA.swift忘年回登壇資料を文章化したものになります。

以下で出てくる環境はXcode9.2bata、Swift4.0です。AutoLayoutにはPureLayoutを部分的に使用しています。

サンプルコードはGitHubに上げています。
https://github.com/akatsuki174/OutsideSafeAreaLayout

SafeAreaについておさらい

これから話の中で頻出するSafe Areaについて念のためさらっと確認です(とは言っても説明難しい)。

  • WWDC2017で登場
  • iOS11, Xcode9から使用可能
  • top及びbottom layout guideのdeprecated化と入れ替わり

Apple公式ではSafe Areaの領域を以下のように説明しています。
e5aca39a-f9a2-4ab8-9f45-08fd95fb845c.png
出典:https://developer.apple.com/documentation/uikit/uiview/positioning_content_relative_to_the_safe_area

※以前「Auto Layout Techniques in Interface Builder」というWWDC2017のセッションをざっくり訳した記事を書いたのですが、その中にもSafe Areaは登場しています。

今回実現したかったUI

スクリーンショット 2017-12-08 22.37.04.png

条件としては以下の通りです。

  • 画面下部に、上下アニメーションするviewのセット(以下MenuViewと呼ぶ)を置く
  • MenuViewの周辺の景観を乱さない

この実装をする上で3点ほど躓きポイントがあったので症状とその対策について書いていきます。

こんな問題が起きたよ集

症状1:MenuViewがHome Indicatorと被る

状況

スクリーンショット 2017-12-08 22.37.04.pngスクリーンショット 2017-12-09 17.19.43.png

ロックを解除する時に使う、画面下部にあるバーのことをHome Indicatorと呼ぶらしいですが、単純に画面下部にviewを貼り付けたらこれと被ってしまうという現象が発生しました。ボタン押せるけど、押せない。理想としてはSafeAreaの中にこのMenuViewを収めておきたいです。

元々の実装

MenuViewのbottomの制約は以下のように付けていました。

MenuView.swift
private var bottomInset: NSLayoutConstraint!
ViewController.swift
self.view.addSubview(menuView)
menuView.bottomInset = menuView.autoPinEdge(toSuperviewEdge: .bottom)

superview(ViewController.view)のbottomとMenuViewのbottomを合わせる制約を付けていました。

改修後の実装

if #available(iOS 11, *) {
    let guide = view.safeAreaLayoutGuide
    menuView.bottomInset = menuView.bottomAnchor.constraint(equalTo: guide.bottomAnchor)
    menuView.bottomInset.isActive = true
} else {
    menuView.bottomInset = menuView.autoPinEdge(toSuperviewEdge: .bottom)
}

iOS11以上であればSafeAreaの概念が登場しているのでそれをコードで取得して、superviewのbottomではなくSafeAreaのbottomに対して制約をかけます。そうすることでMenuViewのbottomがHome Indicatorの上に来てくれます。

症状2:viewの下にtable viewの内容がチラ見えする

状況

スクリーンショット 2017-12-09 17.10.53.png

MenuViewの下にtable viewが見えています。スクロールすると心なしか目がチカチカします。

元々の実装

storyboardを見てみたらbottomがSafeAreaのbottomに対してではなくSuperview(ViewController.view)のbottomに対してかかっていました。

改修後の実装

今回は解決策としてtable viewのbottomをSafeAreaのbottomにしてみます。

スクリーンショット 2017-12-09 18.07.40.png

MenuViewの下の白い部分はViewControllerのviewの背景色になります。

症状3:MenuViewをフルで表示しない時にかっこ悪い

状況

MenuViewを下にしまった時、こんな表示になっています。

スクリーンショット 2017-12-10 0.19.58.png

このスクショだとまだいいのですが、黄色の部分にコンテンツが表示されていることを想定してください。中途半端に見えていてなんだか微妙な気持ちになります。

改修後の実装

今回取った対応としては「↑↓」ボタンがタップされた時にアニメーションしつつalpha値を変更する、というものです。コードで表現すると以下のようになります。

UIView.animate(withDuration: 0.3,
               delay: 0.0,
               options: .curveEaseIn,
               animations: {
                   self.view.alpha = self.shouldShowFull ? 1.0 : 0.0
               },
               completion: nil)

これで目標としていたUIに辿り着けました🎉

おまけ

症状

gifは貼れないので言葉で説明するんですが、以下の症状が発生したことがありました。

  • 動的な制約をかけているセルのレイアウトがSafe Areaの境界付近でうにょうにょ動く
    • 画像/文言があるかないかで画像/文言領域を表示したりしなかったりするセル
    • Home Indicatorを通過する時にconstantの値が-x → 想定値、想定値 → -xにアニメーションするイメージ
  • iPhone X以外は何も問題なく動く(それゆえ気づくの遅れた)

解決策

スクリーンショット 2017-12-10 0.19.58.png

該当のcell.xibのviewを選択し、Safe Area Layout Guideのチェックボックスを外すことで解決しました。

まとめ:iPhone X対応のコツ

  • レイアウトに関わる実装をした時は必ずiPhone Xでも確認する
  • なんかおかしいと思ったらSafe Area Layout Guideのチェックボックスを外してみる
  • Safe Areaの外側がどのような表示になっているべきか考える