UINavigationControllerでStoryboardでセグエを繋げずにNavigationBarを表示させる

# はじめに
こんにちは。
UINavigationControllerについて
少しだけ気になったので、メモとしてまとめて投稿してみました。作成時(Xcode8.2.1)(Swift3.0.2)
至らぬ点など多々あると思いますが、コメントなど頂けたら幸いです。


# サンプル

![NavigationController.gif](https://qiita-image-store.s3.amazonaws.com/0/128250/f1c042fc-8895-87f8-7811-dfea48d37b2a.gif)

今回は
NavigationController × 1
UIViewController × 2
を用いて**show**や**pop**と遷移をして繋がりのある画面遷移を実現します。

<img width="330" alt="スクリーンショット 2017-01-02 20.08.40.png" src="https://qiita-image-store.s3.amazonaws.com/0/128250/35d58330-aece-5173-6287-67edd669ff53.png">

このように**segue**で繋げて1つのStoryboardで管理するのも良いですが、
今回はこれを分割して管理してみたいと思います。

# コードとStoryboardで分割する
## Storyboardの準備
1つのUIViewControllerにつき1つのStoryboardを用意します。
今回はピンク色のUIViewController(以下ピンク色と略)を**ルート**に設定したいので、StoryboardにてUIViewControllerを選択し、
Editor -> Embed In -> Navigation Controller
でNavigationControllerがピンク色と繋がります。
※ここでは**UIView**と**UIButton**を設置しました。

<img width="436" alt="スクリーンショット 2017-01-02 20.10.27.png" src="https://qiita-image-store.s3.amazonaws.com/0/128250/318cdaba-1c36-e55b-1216-561a42249298.png">

繋げると、ピンク色にNavigationBarが表示されます。
UIViewの上部は**TopLayoutGuide.Bottom**へ0の制約を与え、NavigationBarにぴったり付いています。

---

次に遷移先のUIViewControllerを用意します。
今回は青色のUIViewController(以下青色と略)にしました。
こちらもStoryboardを新規作成し、UIViewControllerを設置します。
※ここでも**UIView**と**UIButton**を設置しました。

<img width="225" alt="スクリーンショット 2017-01-02 20.10.00.png" src="https://qiita-image-store.s3.amazonaws.com/0/128250/d22f5ce9-a953-d70f-69af-6f81241e26ac.png">

<img width="241" alt="スクリーンショット 2017-01-02 19.54.54.png" src="https://qiita-image-store.s3.amazonaws.com/0/128250/65314744-6cb7-ec5f-49ef-a39ef86b9815.png">

**Simulated Metrics**で**Top Bar**を**Translucent Navigation Bar**に設定するとNavigationBarがある状態が確認できるので、見やすくなると思います。
(Top Barを設定しなくても、NavigationControllerを持つViewControllerからshowで遷移をすれば実行時にはNavigationBarが表示されます。乗っているUIViewなどに**TopLayoutGuide.Bottom**への制約がある場合、UIViewはNavigationBarからの距離になります)

|TopBarの種類|詳細|
|:-:|:-:|
|Translucent Navigation Bar|半透明のNavigationBar,高さは約64|
|Translucent Navigation Bar With Prompt|Promptの分スペースが空いた半透明のNavigationBar,高さは約94|
|Translucent Black Navigation Bar|黒色テーマで半透明のNavigationBar,高さは約64|
|Translucent Black Navigation Bar With Prompt|Promptの分スペースが空いた黒色テーマで半透明のNavigationBar,高さは約94|
|Opaque Navigation Bar|不透明のNavigationBar,高さは約64|
|Opaque Navigation Bar With Prompt|Promptの分スペースが空いた不透明のNavigationBar,高さは約94|
|Opaque Black Navigation Bar|黒色テーマで不透明のNavigationBar,高さは約64|
|Translucent Black Navigation Bar With Prompt|Promptの分スペースが空いた黒色テーマで不透明のNavigationBar,高さは約94|

ここでの設定はあくまでStoryboard上の**見た目**なので、実際の設定はStoryboard上でNavigationControllerの持つ**NavigationBar**を設定したり、コードで設定する必要があります。




## Swiftファイルの準備
画面遷移のコードは以下のようになっています。

|遷移|コード|
|:-:|:-:|
|show(次へ)|navigationController?.show(_:sender:)|
|pop(戻る)|navigationController?.popViewController(animated:)|

**ピンク色のViewController**

```PinkViewController.swift
import UIKit

class PinkViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    @IBAction func nextAction(_ sender: Any) {
        //Stroyboard生成
        let storyboard = UIStoryboard(name: "Blue", bundle: nil)
        //IB上でInitialの矢印をつけたVCを生成
        let blueVC = storyboard.instantiateInitialViewController()!
        navigationController?.show(blueVC, sender: nil)
    }
}
```

**青色のViewController**

```BlueViewController.swift
import UIKit

class BlueViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    @IBAction func backAction(_ sender: Any) {
        //前のViewControllerを取得
        if let pinkVC = navigationController?.viewControllers[0] as? PinkViewController {
            //なんらかの処理
        }
        _ = navigationController?.popViewController(animated: true)
    }
}
```

# Storyboard Referenceで分割する
## Storyboardの準備
ピンク色のあるStoryboardに**Storyboard Reference**を置き、
Segueとして結びます。遷移先のViewControllerを利用できるようにSegueにIDもつけます。

<img width="472" alt="スクリーンショット 2017-01-03 14.57.11.png" src="https://qiita-image-store.s3.amazonaws.com/0/128250/91c4f16a-899a-6496-709d-2d2d4f75e3da.png">

<img width="270" alt="スクリーンショット 2017-01-03 14.49.43.png" src="https://qiita-image-store.s3.amazonaws.com/0/128250/7a1999da-a47d-7db4-ba1e-273b0c767fae.png"><img width="253" alt="スクリーンショット 2017-01-03 14.45.54.png" src="https://qiita-image-store.s3.amazonaws.com/0/128250/667e8ca2-ef48-d70c-7c08-ef8021a38dec.png">

## Swiftファイルの準備
画面遷移のコードは以下のようになっています。

**ピンク色のViewController**

```PinkViewController.swift
import UIKit

class PinkViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        //セグエのIDから判別
        if (segue.identifier == "toBlue") {
            guard let blueVC = segue.destination as? BlueViewController else {
                return
            }
            //なんらかの処理
        }
    }
}
```

**青色のViewController**

```swift
import UIKit

class BlueViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    @IBAction func backAction(_ sender: Any) {
        //前のViewControllerを取得
        if let pinkVC = navigationController?.viewControllers[0] as? PinkViewController {
            //なんらかの処理
        }
        _ = navigationController?.popViewController(animated: true)
    }
}
```

# NavigationBarの設定
## NavigationBarをStoryboard上で設定する場合
UINavigationControllerはNavigationBarを持っていることをStoryboard上で確認できます。
設定をすると**pop**や**show**と繋がるUIViewController全てに反映されます。
(1つ1つに設定をする場合はコードで行う必要があります。)

<img width="196" alt="スクリーンショット 2017-01-03 12.37.51.png" src="https://qiita-image-store.s3.amazonaws.com/0/128250/67838a07-1166-7985-cac9-f5bebaf22cc0.png"><img width="246" alt="スクリーンショット 2017-01-02 22.08.52.png" src="https://qiita-image-store.s3.amazonaws.com/0/128250/37f5e7a4-bcc1-e7f7-4147-42722c38c3a1.png">

## NavigationBarをコードで設定する場合
上でも記させて頂きましたが、NavigationControllerを持つViewControllerから遷移をすれば実行時にはNavigationBarが表示されます。よってそれぞれのViewController内で以下のコードを記述することで設定することができます。(以下は設定例)

**Style(黒色テーマなどのテーマ設定)**

```swift
navigationController?.navigationBar.barStyle = .default
```

**Translucent(半透明であるかどうかの設定)**

```swift
navigationController?.navigationBar.isTranslucent = true
```

**Bar Tint(NavigationBarの色の設定)**

```swift
navigationController?.navigationBar.barTintColor = UIColor.clear
```

**Shadow**(背景画像と一緒に設定する必要があり、NavigationBarの影の設定)

```swift
navigationController?.navigationBar.setBackgroundImage(UIImage(named: "名前"), for: .default)
```

**Back**(Back Maskと一緒に設定する必要があり、戻るボタンの設定)

```swift
navigationController?.navigationBar.backIndicatorImage = UIImage(named: "名前")
```

**Back Mask**

```swift
navigationController?.navigationBar.backIndicatorTransitionMaskImage = UIImage(named: "名前")
```

**Title Font,Color,Shadow(タイトル文字列の設定)**

```swift
let shadow = NSShadow()
shadow.shadowOffset = CGSize(width: 2, height: 2)
shadow.shadowColor = UIColor.green
shadow.shadowBlurRadius = 5
navigationController?.navigationBar.titleTextAttributes = [
    //フォント
    NSFontAttributeName:UIFont.systemFont(ofSize: 18),
    //文字色
    NSForegroundColorAttributeName: UIColor.red,
    //影
    NSShadowAttributeName: shadow
]
```

# NavigationItemの設定
## NavigationItemをStoryboard上で設定する場合
**NavigationControllerと繋がっているルートとなるViewController**はNavigationItemを持っていることをStoryboard上で確認できます。

<img width="197" alt="スクリーンショット 2017-01-03 13.04.25.png" src="https://qiita-image-store.s3.amazonaws.com/0/128250/a042cb8e-8fdf-fde2-b086-3e9d7aba97eb.png"><img width="256" alt="スクリーンショット 2017-01-03 13.05.08.png" src="https://qiita-image-store.s3.amazonaws.com/0/128250/64730789-09fd-4261-a6fc-d537687ccb4c.png">

---
ルート以外のViewControllerにはStoryboard上で明示的に**NavigationItem**を入れてあげる必要があります。

<img width="282" alt="スクリーンショット 2017-01-03 13.12.21.png" src="https://qiita-image-store.s3.amazonaws.com/0/128250/9b8f0146-b78b-350c-40df-b76b73f901b1.png"><img width="253" alt="スクリーンショット 2017-01-03 13.14.41.png" src="https://qiita-image-store.s3.amazonaws.com/0/128250/2addf11c-0fb0-1335-af08-7ae358cd8ac9.png">

## NavigationItemをコードで設定する場合
それぞれのViewController内で以下のコードを記述することで設定することができます。(以下は設定例)

**Title(タイトルとなる文字列の設定)**

```swift
navigationItem.title = "タイトル"
```

**Prompt(タイトルの上に表示される文字列の設定)**

```swift
navigationItem.prompt = "プロンプト"
```

**Back Button(戻るボタンの文字列)**
ルートのViewController

```swift
let backButtonItem = UIBarButtonItem(title: "戻る", style: .plain, target: nil, action: nil)
navigationItem.backBarButtonItem = backButtonItem
```

それ以外のViewController

```swift
navigationItem.backBarButtonItem?.title = "戻る"
```

次の画面で表示される戻るボタンの文字列の設定になります。
(ピンク色で設定すると青色で表示される)

# デフォルトの戻るボタンについて
現状、青色のViewControllerには**<< Back**というUIButtonを付けているので、
戻るボタンが押された時の処理を記述できるのですが、UINavigationControllerからデフォルトで作成される戻るボタンのアクションを取ることが難しく、
もしも戻るボタンが押された時に行いたい処理がある場合は

**didMove**で親コンテナが存在するかどうかでの方法

> 戻るボタンが押されてすぐの検知はできないのですが、親へと遷移完了したことの検知ができます。
以下はコード例です。

> ```swift
//戻るボタンを押した瞬間ではないが、押されて遷移された時の処理
override func didMove(toParentViewController parent: UIViewController?) {
    super.willMove(toParentViewController: parent)
    if parent == nil {
        print("1つ前のViewControllerに遷移完了")
    }
}
```

**NavigationBarを作成する方法**
> - デフォルトで用意される矢印が利用できないこと
- StoryboardでルートのViewControllerは消すことのできない**Navigation Item**を持つこと。

> が気になりますが、NavigationControllerをStoryboardで繋げるとNavigationBarが自動的に表示されるのでNavigationControllerを選択し、**Bar Visibiloty**をOFFにすると良いかと思います。

> <img width="243" alt="スクリーンショット 2017-01-03 4.24.56.png" src="https://qiita-image-store.s3.amazonaws.com/0/128250/f582ebe4-5277-027e-7d5b-f60d60694312.png">

以上のような方法があるみたいです。

# 参考にさせていただいた記事
戻るボタンのイベントについて
> [ViewControllerで戻るボタンのタップを検知する Qiita](http://qiita.com/yimajo/items/d9409d83fecf9875061d)

UINavigationControllerについて
> [Swift Docs]
(https://sites.google.com/a/gclue.jp/swift-docs/ni-yinki100-ios/uikit/uinavigationcontrollerno-biao-shi)

見て頂いてありがとうございます。