iOS11ライクなUINavigationBarを実装する

iOS11がリリースされて久しいですが、iOS11では標準アプリのデザインがiOS10で変わったミュージックアプリのようにナビゲーションバーのタイトルの位置が変り、フォントサイズも大きくなりました。

<img src="https://qiita-image-store.s3.amazonaws.com/0/14751/a95bab00-9fae-5214-c919-d45ca75ca8e8.png" width=200>

また、iOS11ではサーチバーもナビゲーションバーに統合されたデザインとなっています。
<img src="https://qiita-image-store.s3.amazonaws.com/0/14751/a0838a70-5ae6-b5e6-8bf2-48ff16e2af05.png" width=200>

今回はこれらの実装方法について書こうと思います。

まず、ナビゲーションバーのタイトルから。
iOS11の標準アプリのナビゲーションバーはほとんど全部上のスクリーンショットのように左寄りの大きなフォントになっていますが、アプリを実装する際、デフォルトでは従来の中央に表示されるデザインになります。
これをiOS11ライクにするにはStoryboard、コードのどちらでも可能です。
Storyboardで設定する場合、インスペクタでNavigation Barの「Prefers Large Titles」にチェックを入れれば完了です。

![スクリーンショット 2017-11-26 1.29.58.png](https://qiita-image-store.s3.amazonaws.com/0/14751/45f51860-808e-db35-5ad3-3873fa87c86e.png)

チェックを入れるとそのNavigationControllerに紐づくViewControllerのタイトルが自動で切り替わります。
コードで実装したい場合は以下の1行を書いてあげれば完了です。

```
navigationController?.navigationBar.prefersLargeTitles = true
```
これでStoryboardで設定したい場合と同様、NavigationControllerに紐づくViewControllerのタイトルが自動で切り替わります。
全部大きなタイトルでいい場合はこれでOKなのですが、画面によってタイトルの表示を変えたい場合は [UINavigationItem#largeTitleDisplayMode](https://developer.apple.com/documentation/uikit/uinavigationitem/2909056-largetitledisplaymode) を設定することで実現できます。
これはStoryboard上では設定できない（RootとなっているViewController以外はNavigationItemのインスペクタが表示されないから）のでコードで実装する必要があります。
`largeTitleDisplayMode` には以下の3つが設定できます。

|case|description|
|---|---|
|automatic|前の画面の表示を継承する|
|always|常に大きいタイトルで表示する|
|never|大きなタイトルにはならない|

MainViewControllerからDetailViewControllerへ遷移する場合、
ViewDidLoadでそれぞれ `navigationItem.largeTitleDisplayMode` に `.always` と `.never` を設定してあげればOKです。
※以下ではないので注意！この書き方だと `largeTitleDisplayMode` は反映されません。

```
navigationController?.navigationItem.largeTitleDisplayMode = .never
```

次にサーチバーです。
これも簡単で、`UISearchController` を [UINavigationItem#searchController](https://developer.apple.com/documentation/uikit/uinavigationitem/2897305-searchcontroller)にセットするだけです。

```
let searchController = UISearchController(searchResultsController: nil)
navigationItem.searchController = searchController
```

注意すべき点は、`UINavigationItem#searchController` は画面表示時は隠れていて画面を下に引っ張ることで出てきます。
なので検索が重要な画面では

```
navigationItem.hidesSearchBarWhenScrolling = false
```
と設定して、サーチバーが常に表示された状態にしておくのが良いでしょう。
`hidesSearchBarWhenScrolling` はスクロールされたときにサーチバーを隠すかどうかのフラグです。
`false` にするとサーチバーが常に表示されます。
標準アプリだと、設定が引っ張って出すUIで、連絡先は常に表示されたUIになっていたりします。
<img src="https://qiita-image-store.s3.amazonaws.com/0/14751/8f73b4a6-f804-d7a7-d702-2d94bd0f872a.png" width=200>　<img src="https://qiita-image-store.s3.amazonaws.com/0/14751/b4fd1f85-a8db-d164-baa6-ab2d24330b74.png" width=200>

サーチバーが画面表示時には出ているが、画面をスクロールすると消えるという実装は標準では用意されていないようです。
常に表示されていると画面の領域が狭くなるのでそれが許容できない場合は `UINavigationItem#searchController` を使うのをやめたほうが良さそう。

ちなみにテーブルビューにサーチバーとRefreshControlがあるUIは一般的だと思いますが、RefreshControlを有効にしている場合、大きくタイトルを表示している場合と従来の中央にタイトルを表示している場合でインジケータの表示が異なります。
<img src="https://qiita-image-store.s3.amazonaws.com/0/14751/c410084e-7518-927b-cfcb-a66f8f1cb73c.png" width=200>　<img src="https://qiita-image-store.s3.amazonaws.com/0/14751/b367fbdc-2216-8f07-5b90-fe6f63ebdd9b.png" width=200>
ただし、これらはOSが勝手にやってることなので、実装時は何も気にすることなく普通にRefreshControlを実装すればOKです。

以上、iOS11ライクなUINavigationBarの実装Tipsでした。