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

  • 2
    Like
  • 0
    Comment

はじめに

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

サンプル

NavigationController.gif

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

スクリーンショット 2017-01-02 20.08.40.png

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

コードとStoryboardで分割する

Storyboardの準備

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

スクリーンショット 2017-01-02 20.10.27.png

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


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

スクリーンショット 2017-01-02 20.10.00.png

スクリーンショット 2017-01-02 19.54.54.png

Simulated MetricsTop BarTranslucent 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もつけます。

スクリーンショット 2017-01-03 14.57.11.png

スクリーンショット 2017-01-03 14.49.43.pngスクリーンショット 2017-01-03 14.45.54.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

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上で確認できます。
設定をするとpopshowと繋がるUIViewController全てに反映されます。
(1つ1つに設定をする場合はコードで行う必要があります。)

スクリーンショット 2017-01-03 12.37.51.pngスクリーンショット 2017-01-02 22.08.52.png

NavigationBarをコードで設定する場合

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

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

navigationController?.navigationBar.barStyle = .default

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

navigationController?.navigationBar.isTranslucent = true

Bar Tint(NavigationBarの色の設定)

navigationController?.navigationBar.barTintColor = UIColor.clear

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

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

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

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

Back Mask

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

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

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上で確認できます。

スクリーンショット 2017-01-03 13.04.25.pngスクリーンショット 2017-01-03 13.05.08.png


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

スクリーンショット 2017-01-03 13.12.21.pngスクリーンショット 2017-01-03 13.14.41.png

NavigationItemをコードで設定する場合

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

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

navigationItem.title = "タイトル"

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

navigationItem.prompt = "プロンプト"

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

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

それ以外のViewController

navigationItem.backBarButtonItem?.title = "戻る"

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

デフォルトの戻るボタンについて

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

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

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

//戻るボタンを押した瞬間ではないが、押されて遷移された時の処理
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にすると良いかと思います。

スクリーンショット 2017-01-03 4.24.56.png

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

参考にさせていただいた記事

戻るボタンのイベントについて

ViewControllerで戻るボタンのタップを検知する Qiita

UINavigationControllerについて

Swift Docs

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