今年、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の領域を以下のように説明しています。
出典:https://developer.apple.com/documentation/uikit/uiview/positioning_content_relative_to_the_safe_area
※以前「Auto Layout Techniques in Interface Builder」というWWDC2017のセッションをざっくり訳した記事を書いたのですが、その中にもSafe Areaは登場しています。
今回実現したかったUI
条件としては以下の通りです。
- 画面下部に、上下アニメーションするviewのセット(以下MenuViewと呼ぶ)を置く
- MenuViewの周辺の景観を乱さない
この実装をする上で3点ほど躓きポイントがあったので症状とその対策について書いていきます。
こんな問題が起きたよ集
症状1:MenuViewがHome Indicatorと被る
状況
ロックを解除する時に使う、画面下部にあるバーのことをHome Indicatorと呼ぶらしいですが、単純に画面下部にviewを貼り付けたらこれと被ってしまうという現象が発生しました。ボタン押せるけど、押せない。理想としてはSafeAreaの中にこのMenuViewを収めておきたいです。
元々の実装
MenuViewのbottomの制約は以下のように付けていました。
private var bottomInset: NSLayoutConstraint!
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の内容がチラ見えする
状況
MenuViewの下にtable viewが見えています。スクロールすると心なしか目がチカチカします。
元々の実装
storyboardを見てみたらbottomがSafeAreaのbottomに対してではなくSuperview(ViewController.view)のbottomに対してかかっていました。
改修後の実装
今回は解決策としてtable viewのbottomをSafeAreaのbottomにしてみます。
MenuViewの下の白い部分はViewControllerのviewの背景色になります。
症状3:MenuViewをフルで表示しない時にかっこ悪い
状況
MenuViewを下にしまった時、こんな表示になっています。
このスクショだとまだいいのですが、黄色の部分にコンテンツが表示されていることを想定してください。中途半端に見えていてなんだか微妙な気持ちになります。
改修後の実装
今回取った対応としては「↑↓」ボタンがタップされた時にアニメーションしつつ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以外は何も問題なく動く(それゆえ気づくの遅れた)
解決策
該当のcell.xibのviewを選択し、Safe Area Layout Guideのチェックボックスを外すことで解決しました。
まとめ:iPhone X対応のコツ
- レイアウトに関わる実装をした時は必ずiPhone Xでも確認する
- なんかおかしいと思ったらSafe Area Layout Guideのチェックボックスを外してみる
- Safe Areaの外側がどのような表示になっているべきか考える