【随時更新】iPhoneX完全対応マニュアル

# はじめに
この記事は既存、もしくは新規で開発するiOSアプリをiPhoneXに対応するための知見をまとめたものです。
新しい情報やTipsをコメントいただけると随時更新させていただきますので、よろしくお願いします！

## iPhoneXについて

- 2017年9月12日に開催されたApple Special Eventにて発表された最新のiPhone
- 顔認証でロック解除ができる「Face ID」を搭載
- **ベゼルレスディスプレイ**
    - 今までのデバイスと違って、**四隅が丸くなっていたり、フロントカメラ部分の凹みがディスプレイに被っていたりする**ので、このようなディスプレイに対応するために**セーフエリア**の概念が新しく追加された

<img width="750" alt="iPhoneXの凹み" src="https://qiita-image-store.s3.amazonaws.com/0/46227/d14ff991-488b-92f4-52b4-36e3087526f5.png">

## セーフエリアについて
- AppleがiOS11から導入した概念
- ナビゲーションバーやステータスバーなどの外側のUIに被らない安全な領域を取得できる
- 使用するには**Xcode9+、iOS11+ SDK**が必要
- セーフエリアの領域やマージン幅については[usagimaruさん](https://qiita.com/usagimaru)の以下の記事がとても参考になる
    - [iPhone Xのセーフエリアやマージン幅について - Qiita](https://qiita.com/usagimaru/items/761e9a5f3d78b1939df8)

<img width="750" alt="セーフエリア" src="https://qiita-image-store.s3.amazonaws.com/0/46227/21c48a5c-f168-ba4f-37e7-a41bc0379d6b.png">

# iPhoneX対応 Tips集
ここからは対応するためのTipsを紹介していきます。

## Storyboardでセーフエリアを有効にするには
1. 任意のStoryboardを開く
2. 右側にあるInterface Builderのインスペクタを開く
3. 「**Use Safe Area Layout Guides**」のチェックをONにする
4. セーフエリア(UILayoutGuide) が追加される

<img width="300" alt="Use Safe Area Layout Guidesのチェッカー" src="https://qiita-image-store.s3.amazonaws.com/0/46227/6f52e001-6f3d-d96e-b2ba-77759a455020.png">

- セーフエリアを有効にしていなかったStoryboardでセーフエリアを有効にすると、Top Layout GuideやBottom Layout Guideに接続していたビューは自動でセーフエリアのTop、Bottomに差し替えてくれる

<img width="750" alt="セーフエリアの適用前後" src="https://qiita-image-store.s3.amazonaws.com/0/46227/5d6b949f-0f3a-97a8-1edf-0b18e4ac77dd.png">

## ソースコード上からセーフエリアを取得したい

- iOS11からは上記のStoryboardでセーフエリアを有効にしたときに追加されるUILayoutGuideがUIViewのプロパティに [**safeAreaLayoutGuide (UILayoutGuide)**](https://developer.apple.com/documentation/uikit/uiview/2891102-safearealayoutguide) として追加されている
- iOS11からはUIViewのプロパティに [**safeAreaInsets (UIEdgeInsets)**](https://developer.apple.com/documentation/uikit/uiview/2891103-safeareainsets) が追加されていて、そこからセーフエリアのマージンを取得できる
- iOS11+しか使えないプロパティのため、** #available(iOS 11, *) での分岐**が必要
- 以下の[hituziandoさん](https://qiita.com/hituziando)の記事によると、UIViewControllerのライフサイクルで一番最初にsafeAreaInsetsが確定するのは**viewWillLayoutSubviews**
    - [【iOS11】safeAreaInsetsの値が取得できるタイミング](https://qiita.com/hituziando/items/e5873b5bfa42247071e6)

```swift:ViewController.swift
import UIKit

class ViewController: UIViewController {

    override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()

        // iOS11かどうかで分岐する
        let safeAreaInsets: UIEdgeInsets
        if #available(iOS 11, *) {
            safeAreaInsets = view.safeAreaInsets
        } else {
            safeAreaInsets = .zero
        }
        print("safeAreaInsets: \(safeAreaInsets)")
    }
}
```

## ソースコード上からセーフエリアの変化を感知したい

- iOS11からはセーフエリアの変化を感知するために**UIView**に **open func safeAreaInsetsDidChange()** が、**UIViewController**に **open func viewSafeAreaInsetsDidChange()** が追加されている
    - それぞれ、オーバーライドすることで、メソッド内でセーフエリアの変化を感知できる
- このメソッドを便利に使えるようにする拡張方法などは[marty-suzukiさん](https://qiita.com/marty-suzuki)の以下の記事が参考になる
    - [safeAreaInsetsの変更を感知する - Qiita](https://qiita.com/marty-suzuki/items/f3dcab7624c9079d74ab)

## セーフエリアだけで切り取るだけのデザインは非推奨
- 上下に常に黒いバーが存在するような、セーフエリアで切り取るだけのデザインのアプリは**Appleが提供する標準アプリと矛盾したデザインになってしまうため**、非推奨となっている

<img width="750" alt="切り取るだけは非推奨" src="https://qiita-image-store.s3.amazonaws.com/0/46227/1cba6618-8778-e28c-3c2b-d30ccf2ae9bc.png">

- 以下の画像のように、**上下の背景とコンテンツが溶け込むような意識**をする必要がある

<img width="750" alt="Appleの提供する標準アプリの例" src="https://qiita-image-store.s3.amazonaws.com/0/46227/c1164580-faca-05f7-7bfc-9ec5f562433f.png">


## 必要な箇所でclipsToBoundsがtrueになっているかを注意する

- 今までのデバイスだと、ディスプレイの外に配置しておけば見えなくなるので、安心して画面外に置いていたビューが、セーフエリアによってあられもない姿にされてしまう
- 見切れさせたくない箇所は、しっかりと**clipsToBoundsをtrueに**しておく必要がある

<img width="750" alt="clipsToBoundsの見直し" src="https://qiita-image-store.s3.amazonaws.com/0/46227/69071f9f-2856-f93e-9de2-a4e851780511.png">


## コンテンツをフルスクリーン表示する際にはアスペクト比を保つ

- iPhoneXでコンテンツをフルスクリーンで表示する際には、iPhone8を基準にした場合、**左右を切り取って上下を埋めて表示するか、左右を合わせて上下を凹ませて表示するか**して、アスペクト比は保つようにする必要がある

<img width="750" alt="コンテンツをフルスクリーン表示する際にはアスペクト比を保つ" src="https://qiita-image-store.s3.amazonaws.com/0/46227/fb5c38f5-6b38-f640-3666-ec9cb8398740.png">


## iPhoneXの下のバーを消したい

- バーの正式名称は**Home Indicator**
- iOS11からはHome Indicatorを非表示にするためのメソッド(`open func prefersHomeIndicatorAutoHidden() -> Bool`)が**UIViewController**に追加されている
    - オーバーライドしてtrueを返せば、Home Indicatorは**画面に一定時間触らなければ**、自動的に非表示になる
    - 非表示になってから画面に触れると、Home Indocatorはすぐに表示される
- Home Indicatorは基本表示されていないとユーザーに混乱を招くため、画像や動画をフルスクリーン表示する際など、**Home Indocatorが被ることでコンテンツの表示に影響が出てしまう場合以外は非表示にしない**ようにする

```swift:ViewController.swift
import UIKit

class ViewController: UIViewController {
    private var isHiddenHomeIndicator: Bool = true

    override func viewDidLoad() {
        super.viewDidLoad()

        if #available(iOS 11, *) { 
            // HomeIndicatorの表示を更新するためのメソッド
            setNeedsUpdateOfHomeIndicatorAutoHidden() 
        }
    }

    @available(iOS 11, *)
    override func prefersHomeIndicatorAutoHidden() -> Bool {
        return isHiddenHomeIndicator
    }
}
```

<img width="750" alt="Home Indicator Auto Hidden" src="https://qiita-image-store.s3.amazonaws.com/0/46227/fcbddf3d-f39e-5ae6-b57a-bb915d6281d3.gif">

# セーフエリアに対応済みのレイアウト系ライブラリ一覧
- [robb/Cartography](https://github.com/robb/Cartography)
    - [yysskkさん](https://github.com/yysskk)の[プルリク](https://github.com/robb/Cartography/pull/267)がマージされたver.`2.1.0`からサポートされている
- [marty-suzuki/MisterFusion](https://github.com/marty-suzuki/MisterFusion)
    - 開発者本人の[コミット](https://github.com/marty-suzuki/MisterFusion/commit/4e22003dd314fca710353b9e11d27434199f7b62)が反映されたver.`3.1.0`からサポートされている
- [SnapKit/SnapKit](https://github.com/SnapKit/SnapKit)
    - [公式ドキュメント](http://snapkit.io/docs/)にて、safeAreaLayoutGuideへの対応方法が追加されている


# 参考リンク一覧
- [アイフォンX、凹みを切るか？凹みを使うか？ ~iPhoneX対応のススメ~ - Speaker Deck](https://speakerdeck.com/cokaholic/aihuonx-ao-miwoqie-ruka-ao-miwoshi-uka-iphonexdui-ying-falsesusume)
- [Designing for iPhone X - Fall 2017 - Videos - Apple Developer](https://developer.apple.com/videos/play/fall2017/801/)
- [iPhone X - Overview - iOS Human Interface Guidelines](https://developer.apple.com/ios/human-interface-guidelines/overview/iphone-x/)
- [iPhone Xのセーフエリアやマージン幅について - Qiita](https://qiita.com/usagimaru/items/761e9a5f3d78b1939df8)
- [【iOS11】safeAreaInsetsの値が取得できるタイミング - Qiita](https://qiita.com/hituziando/items/e5873b5bfa42247071e6)
- [safeAreaInsetsの変更を感知する - Qiita](https://qiita.com/marty-suzuki/items/f3dcab7624c9079d74ab)