UINavigationControllerと座標ズレの小ネタ

画面遷移にUINavigationControllerを使うことは多いと思いますが、少し注意をしないとレイアウトが思い通りにならないことがあります。
自分の経験に基づいて、注意すべき点を2点あげます。

# UINavigationController.navigationBar.isTranslucent
ナビゲーションバーを透過にするかのフラグです。と共に、ビューの開始位置を決めるフラグでもあります。標準では`true`になっています。

----
追記: コメントで指摘をいただきました。@dokubekoさん、ありがとうございます。
>バーの`isTranslucent`がビューの面積をバーの下までのばすトリガーではありません。

>ビューの面積をバーの下まで伸ばすかどうかはビューコントローラの`edgesForExtendedLayout`プロパティで指定します。これに関連し、バーが不透明な場合にもそうするかどうかを`extendedLayoutIncludesOpaqueBars`で指定します。

>`isTranslucent`がビューの面積の拡張のトリガーのようにみえるのは単に`edgesForExtendedLayout`に`top`が含まれてていて`extendedLayoutIncludesOpaqueBars`が`false`の状態で切り替えているからです。`extendedLayoutIncludesOpaqueBars`を`true`にすれば、`isTranslucent`が`false`でもビューはバーの下まで伸びます。

ビューの描画領域を伸ばすのは`UIViewController.edgesForExtendedLayout`みたいです。こいつはデフォルトで`.all`になっています。
`extendedLayoutIncludesOpaqueBars`はデフォルトで`false`になっています。

Storyboardだと、以下で変更できました。
![スクリーンショット 2016-12-20 10.51.50.png](https://qiita-image-store.s3.amazonaws.com/0/84917/2196fbdc-a0f7-52ad-2529-2ab6bb59b99e.png)

`extendedLayoutIncludesOpaqueBars`を`true`にしてみると、たしかに描画範囲がナビゲーションバーのところまで広がります。
![スクリーンショット 2016-12-20 10.59.50.png](https://qiita-image-store.s3.amazonaws.com/0/84917/db377854-c342-3c08-1af8-0943d9babdec.png)

`edgesForExtendedLayout`の方は`.top`と`.bottom`しか設定できないのですが、両方チェックがついている状態だと`.all`になります。
いずれか一つのみチェックをすると`.top`または`.bottom`になり、両方チェックを外すと一切拡張されなくなります。
`.left`と`.right`は一体・・・？また調べます。

とにかく、以下の記事は

```swift
edgesForExtendedLayout = .all
extendedLayoutIncludesOpaqueBars = false
```
という前提でお読みください。

----

例えばこんな画面を作ってみます。
![スクリーンショット 2016-12-19 15.14.05.png](https://qiita-image-store.s3.amazonaws.com/0/84917/2ac66463-5314-b656-bd60-36d41cb58df0.png)
ナビゲーションバーに隠れるように'見えないはず'というラベルを置き、中心に'動かす'ボタン、下に'した'ラベルを置きました。
動かすボタンは以下のように、ナビゲーションバーを出したり隠したりします。

```swift
@IBAction func switchNavigationbarHidden(_ sender: UIButton) {
    
    let status = !self.navigationController!.isNavigationBarHidden
    self.navigationController?.setNavigationBarHidden(status, animated: true)
}
```

まずは`isTranslucent`が`true`の状態で動かしてみます。
![スクリーンショット 2016-12-19 15.19.13.png](https://qiita-image-store.s3.amazonaws.com/0/84917/312e9478-efea-9852-d0ea-5f8c189b1823.png)
ナビゲーションバーが表示されている状態では、'見えないはず'ラベルは見えません。

![スクリーンショット 2016-12-19 15.19.16.png](https://qiita-image-store.s3.amazonaws.com/0/84917/db9b96a2-b952-6d8f-dd46-355ce47879ab.png)
ナビゲーションバーを隠すと、'見えないはず'ラベルが見えるようになりました。

以上から、`isTranslucent`を`true`にすると次のようなことが起きます。

* ナビゲーションバーが透過になります(例だとちょっとわかりにくいけど)
* `ViewController`のビューの描画範囲は、ナビゲーションバーで隠れる部分を含めて画面全体になります

次に、`isTranslucent`を`false`にしてみましょう。
画像ではStoryboard上で`false`にしていますが、もちろんコードからしても構いません。
![スクリーンショット 2016-12-19 15.23.21.png](https://qiita-image-store.s3.amazonaws.com/0/84917/7f9eede2-ca54-25e4-608c-34e358afca64.png)

既にStoryboard上でも変化が起きています。先程はナビゲーションバーに隠されて見えなかった'見えないはず'ラベルが見えるようになりました。
さらに、'動かす'ボタンの位置も先程より少し下になりました。

実行してみます。
![スクリーンショット 2016-12-19 15.26.05.png](https://qiita-image-store.s3.amazonaws.com/0/84917/3794e2a9-4e8d-4806-a7e2-32f000dead1a.png)
ナビゲーションバーが表示されている状態は、Storyboardで見たとおりです。

![スクリーンショット 2016-12-19 15.26.08.png](https://qiita-image-store.s3.amazonaws.com/0/84917/73b0fe80-33d4-2fe8-c257-cc58c853e471.png)
ナビゲーションバーを隠すと、'見えないはず'ラベルと'動かす'ボタンが少し上に移動しました！

以上から、`isTranslucent`を`false`にすると次のようなことが起きます。

* ナビゲーションバーが透過されなくなります
* ナビゲーションバーが表示されている状態では、`ViewController`のビューの描画範囲が狭くなります
* ナビゲーションバーを隠すと、その分`ViewController`のビューの描画範囲が広がります

個人で開発する分にはそこまで問題になることはないと思いますが、複数人で開発する際、ある人は`true`前提、またある人は`false`前提だったりすると、レイアウトがおかしなことになります。
開発を始める前にすり合わせをしておきましょう。

# UIViewController.automaticallyAdjustsScrollViewInsets
スクロールできるビューを置いたときに、スクロール開始位置を自動的にずらしてくれるありがたい(場合によっては迷惑な)機能です。標準では`true`になっています。

次はこんな画面を作ってみます。
![スクリーンショット 2016-12-19 16.10.40.png](https://qiita-image-store.s3.amazonaws.com/0/84917/1c4def85-13ca-cdb2-83b5-e7c237ce177a.png)
画面いっぱいに`TableView`を置いて、そこに一つ`TableViewCell`を追加しました。
`TableView`はナビゲーションバーに隠れる部分まで領域があるのに、`TableViewCell`はナビゲーションバーに隠れること無く表示されています。これがタイトルの自動調整機能なのです。

試しに、`automaticallyAdjustsScrollViewInsets`を`false`にしてみましょう。
画像ではStoryboard上で`false`にしていますが、もちろんコードからしても構いません。
![スクリーンショット 2016-12-19 16.16.47.png](https://qiita-image-store.s3.amazonaws.com/0/84917/bac3c992-4f56-4f24-edd4-beb3b6ca7cd6.png)
`TableViewCell`がナビゲーションバーに隠されました。

この自動調節機能、別にナビゲーションバーに重ならないところに`TableView`を配置しても、しっかり発動してくれます。
![スクリーンショット 2016-12-19 16.20.01.png](https://qiita-image-store.s3.amazonaws.com/0/84917/1d6bb3d8-5075-bc22-bce0-df6e58111074.png)

こういうときはいらないので`false`にしてしまいましょう。
![スクリーンショット 2016-12-19 16.20.27.png](https://qiita-image-store.s3.amazonaws.com/0/84917/d82d451f-da65-ae41-d1f9-83c4a7862d51.png)

Storyboardで作業をしているときは`automaticallyAdjustsScrollViewInsets`の設定が目に見えますが、xibを作っているときなんかは要注意です。
`ScrollView`を置いていて、`contentSize`は`ScrollView`の`frame`より小さいはずなのに、何故かスクロールが発生するなんてときは、`automaticallyAdjustsScrollViewInsets`が悪さしている可能性があります。

# おわりに
今となっては困ることはなくなりましたが、勉強し初めの頃はこういった暗黙の了解的なものにだいぶ苦しめられました。
ネットで得られる断片的な知識だけで開発しようとするとこういった状況に陥りやすい気がします。
公式ドキュメントを読めばいいんですが、なかなか面倒ですよね・・・
とりあえず、`UINavigationController`を使うときは上記2点に気をつけましょう！