はじめに
前回はiOSアプリにおける画面遷移の種類についてお話しました。
画面遷移編2:https://qiita.com/euJcIKfcqwnzDui/items/6d37aaf00c0bc7ce26ca
今回はその具体的な実装方法について説明していきます。
以前モーダル遷移の実装をしたプロジェクトをベースとして説明していきます。
前準備
その前に、今までプロジェクト作成されたときに生成されたViewController.swift
とMain.storyboard
をそのまま使ってきましたが、画面が多くなるとわかりにくくなるので変更しておきましょう。
それぞれFirstViewController.swift
とFirstViewController.storyboard
にリネームします。
まずはファイル名をリネームします。
ファイルを選択しもう一度ファイル名をクリックするとリネームできます。
次はクラス名を変更します。
import UIKit
class FirstViewController: UIViewController { // ViewControllerから変更
/// テキストフィールド
@IBOutlet weak var textField: UITextField!
/// 画面遷移ボタンタップ処理
/// - Parameter sender: ボタン
@IBAction func didTapTransitionButton(_ sender: Any) {
...
...
クラス名を変更すると今までそのクラスを使用していた場所も変更してあげないといけません。
今の状態だとFirstViewController.storyboard
のViewController
オブジェクトに設定されています。
FirstViewController.storyboard
を開きViewController
オブジェクトを選択します。
その状態で[Identity Inspector]を選択し、[class]の項目をFirstViewController
と入力します。
ここで一度シミュレータで実行してみましょう。
すると、起動直後アプリがクラッシュし以下のエラーが表示されます。
Thread 1: Exception: "Could not find a storyboard named 'Main' in bundle NSBundle ...
「Mainという名前のstoryboardが見つからない」とのこと。
Main.storyboard
もリネームしているので当然です。
アプリを起動した後どのstoryboardから表示するのかXcodeに教えてあげなければいけません。
プロジェクトを作成した段階ではMain.storyboard
が指定されています。
これをFirstViewController.storyboard
に変更してあげます。
フォルダ構成の一番上、プロジェクト名を選択します。
ここではプロジェクトの全体的な設定をすることができます。
[TARGETS]にはいくつかありますが一番上のプロジェクト名を選択します。
さらに上のタブで[General]を選択。
[Development Info]に[Main Interface]という項目があるのでプルダウンからFirstViewController.storyboard
を選択してください。
さらにXcode11からInfo.plist
というファイルにも「Main」が設定されています。
その中の[Storyboard Name]の項目を「FirstViewController」と書き換えてください。
これでファイル名の変更は完了です。
シミュレータで実行し問題ないか確認してください。
モーダル
これは以前説明したので省きます。
https://qiita.com/euJcIKfcqwnzDui/items/679b1cd30694519f4916#%E7%94%BB%E9%9D%A2%E9%81%B7%E7%A7%BB%E3%81%99%E3%82%8B
プッシュ
segueを使った実装
プッシュの実装方法について説明します。
プッシュの画面遷移には以前説明したsegueを使った実装をします。
まずはMain.storyboard
を開きます。
プッシュの遷移にはUINavigationController
というクラスを使用します。
このUINavigationController
をStoryboardに追加しましょう。
FirstViewController
を選択します。
その状態でXcode上部のツールバーから[Edior]>[Embed In]>[Navigation Controller]を選択していってください。
FirstViewController
にくっつくような形でNavigation Controller
が追加されました。
Navigation Controller
は画面のような見た目をしていますが、これはあくまでStoryboard上の表示での話であって実際はユーザから見られる画面というわけではありません。
UINavigationController
はControllerとあるようにコントロールするためのクラスです。
Navigationは画面遷移と言い換えてもらえればいいかと思います。
つまりUINavigationController
は画面遷移を制御するためのクラスであって画面そのものではありません。
今のNavigation Controller
を追加した操作はUINavigationController
という概念のようなオブジェクトをFirstViewController
に追加するというイメージです。
もう少し補足するとUIViewController
もControllerとあるように、これ自体が画面というわけではありません。
これも画面をコントロールするオブジェクトで、実際に表示されている画面はFirstViewController
の中にあるView
というUIです。
コントローラは画面の表示制御やユーザからのイベントを扱うという役割です。
少し細かい話をしてしまいました。
実装を進めます。
FirstViewController
にあるsegueを選択してください。
その状態で[Attributes Inspector]を選択すると[Kind]という項目があるので選択します。
ポップアップメニューが表示されるので[Show(e.g. Push)]を選択します。
これでsegueがプッシュ遷移用に変更されました。
Storyboardの設定はこれだけです。
あとはソースコードから処理実行するだけです。
ですがこれはモーダル遷移の場合と同じでperformSegue
を呼び出してあげるだけです。
segueのidentifierを変えていないので以前モーダルを実装したままのコードですでにプッシュ遷移ができます。
該当のコードは以下です。
self.performSegue(withIdentifier: "SecondViewController", sender: nil)
実装は以上です。
シミュレータで実行し動作を確認してみてください。
またパラメータもモーダルと同様の方法で渡すことができます。
let text = textField.text
self.performSegue(withIdentifier: "SecondViewController", sender: text)
UINavigationControllerについて
UINavigationController
では一連のプッシュ遷移における画面を管理します。
UINavigationController
はrootViewController
というプロパティを持ち、プッシュ遷移の一番最初の画面を保持します。
Storyboardから追加した今回の場合だと、UINavigationController
を追加したFirstViewController
がrootViewController
になります。
またUINavigationController
はviewControllers: [UIViewController]
というプロパティを持っており、遷移した画面を配列で管理しています。
今回の例では画面遷移が完了した状態ではviewControllers[0]
にFirstViewController
、viewControllers[1]
にSecondViewController
が格納されています。
プッシュ遷移先のSecondViewController
に[戻る]ボタンが表示されていたかと思います。
[戻る]ボタンをタップすると一画面前に戻ります。
画面を戻るとSecondViewController
はUINavigationController
の管理対象からはずれ解放されます。
つまりviewControllers[1]
は存在しなくなります。
図で表すと以下のようになります。
SecondViewControllerからさらに遷移する
当然SecondViewController
からThirdViewController
という画面にさらにプッシュ遷移した場合管理下に追加されviewControllers[2]
に格納されます。
実装してみます。
これは特に難しい話ではなく同様にSecondViewController
からThirdViewController
へのsegueを作ってあげてperformSegue
を呼び出してあげるだけです。
SecondViewController
を追加した手順と同様にThirdViewController.swift
、ThirdViewController.storyboard
を追加以下のように編集してください。
import UIKit
class ThirdViewController: UIViewController {
}
さらにSecondViewController.storyboard
からThirdViewController
へのsegueをプッシュで作成してください。
またSecondViewController.storyboard
にボタンを追加し、ボタンタップでThirdViewController
に遷移するようにします。
シミュレータで実行すると3画面遷移できるようになります。
/// 画面遷移ボタンタップ処理
/// - Parameter sender: ボタン
@IBAction func didTapTransitionButton(_ sender: Any) {
// 画面遷移
self.performSegue(withIdentifier: "ThirdViewController", sender: nil)
}
ここで注意してほしいことはSecondViewController.storyboard
にUINavigationController
は追加しないということです。
UINavigationController
はrootとなる画面にのみ追加します。
今rootはFirstViewController
としているためSecondViewController
はrootではありません。
SecondViewController
からプッシュ遷移する場合、rootに設定したUINavigationController
が参照されます。
予想がつくかと思いますがThirdViewController
で[戻る]ボタンを押すと以下のような構成になります。
このようにUINavigationController
では最後に追加されたものから消えていきます。
この管理方法を**スタック(stack)と呼びます。
余談ですがスタックは画面管理の方法ではなく配列の管理方法の1つです。
スタックは基本情報試験にも出てくるIT技術の基礎知識なので抑えておきましょう。
別の配列管理方法ではキュー(queue)**があります。一度調べておいてください。
UINavigationControllerの参照のされ方
ここまでの画面遷移処理で少し違和感を覚える方もいるかもしれません。
rootであるFirstViewController
はStoryboardでUINavigationController
のオブジェクトを追加したので画面遷移にUINavigationController
を使えるのはなんとなくわかるかと思います。
ですがSecondViewController
にはUINavigationController
は設定されていないはずなのにどうして使えるのでしょうか?
実はUIViewController
にはnavigationController
というように現在自分が管理されているUINavigationController
をプロパティとして持っています。
画面遷移の具体的な処理はUIKit
が勝手にやってくれているので詳細は不明なのですが、おそらくプッシュ遷移のタイミングで自分のnavigationController
を次の画面に一緒にセットしています。
従って1つのUINavigationController
のオブジェクトが次へ次へと共有されているのです。
図にすると以下のようなイメージになります。
このような形でroot画面からプッシュ遷移した画面の全てを管理しています。
タブ
segueを使った実装
タブ遷移の実装方法について説明します。
タブに関してもsegueを使った実装をします。
タブの遷移にはUITabBarController
というクラスを使用します。
UITabBarController
を扱うために、まずTabViewController.storyboard
というファイルを追加しましょう。
TabViewController.storyboard
を開き、[+]ボタンからUITabBarController
を探しStoryboardに追加します。
Tab Bar Controller
というオブジェクトと一緒にitem 1 Scene
とitem 2 Scene
というオブジェクトが追加されました。
これはタブTab Bar Controller
がデフォルトで表示する画面テンプレートです。
今回は不要なので削除します。
それぞれ選択し削除しておいてください。
Tab Bar Controller
も画面のような形ですが、これも「Controller」とあるように画面そのものではなくタブ遷移において画面を管理するオブジェクトです。
階層1つしたにあるTab Bar
というオブジェクトは画面一番下に表示されるタブ自体です。これは表示されるUIでUITabBar
というクラスです。
このTabBarController.storyboard
で最初に参照するオブジェクトはTabBarController
なので[Is Initial View Controller]のチェックを付けます。
TabBarController
が画面を追加します。
FirstViewController.storyboard
、SecondViewController.storyboard
、ThirdViewController.storyboard
への参照を追加します。
他の画面遷移と同様Storyboard Reference
を画面数だけ配置します。
配置したStoryboard Reference
にそれぞれStoryboardを設定します。
TabBarController
からそれぞれのStoryboard Reference
までsegueを作成します。
segueの種類は[Relationship Segue]の[view controllers]を選択してください。
以下のようになればタブ遷移の準備は完了です。
次はアプリ起動直後に表示するStoryboardを変更します。
先述したMain.storyboard
をリネームする際、説明したようにプロジェクトの[Main Interface]とInfo.plist
の[Storyboard Name]を「TabBarController」に変更してください。
シミュレータで実行するとタブバーが表示されます。
タブバーには何も表示されていませんが、タブボタン自体は配置されています。
適当な場所をタップするとタブ遷移されます。
ですがこれではユーザからすると何のUIかわからないので画像と名前を設定してあげましょう。
SecondViewController.storyboard
を開いてください。
[+]ボタンからUITabBarItem
を探し、SecondViewController
に追加してください。
UITabBarItem
はタブボタンのオブジェクトです。
ここにアイテム名や画像を設定してあげるとそれがタブに表示されます。
UITabBarItem
を選択し、[Attributes Inspector]の[BarItem]に[Title]と[Image]を設定してください。
※画像はまだ追加していないので標準でXcodeに入っているものが出ます。自分で画像を追加していくこともできます。
ThirdViewController.storyboard
にも同様の手順で追加します。
FirstViewController.storyboard
についても本来同様の手順でよかったのですが、今回UINavigationController
が追加されています。
そのためFirstViewController
オブジェクトに追加しても表示されません。
この場合はNavigationController
オブジェクトに追加してあげてください。
[Is Initial ViewController]のチェックを入れたオブジェクトに追加する必要があると覚えましょう。
シミュレータで実行するとタブアイテムが表示されます。
UITabBarControllerについて
UITabBarController
もUINavigationController
と同様に画面を管理するクラスです。
ですがあくまで並列な画面遷移のためrootというものは存在しません。
表示される画面は同様にviewControllers
で管理されています。
タブ遷移ではプッシュ遷移と違い画面を消すということが基本的にはないためviewControllers
はスタック管理されているというわけではありません。
ただ配列として画面を持っているというだけです。
またUIViewController
はtabBarController
を持っており、それぞれのViewControllerが同一のUITabBarController
を参照しています。
プッシュ遷移とタブ遷移の組み合わせ
実際のアプリではプッシュ遷移とタブ遷移両方を組み合わせて画面遷移を実装していきます。
また、今回のFirstViewController
ではすでに両方の遷移をしています。
FirstViewController
にはNavigationController
オブジェクトが追加されているからですね。
今回の画面の構成を正確に書くと以下のようになります。
若干複雑な関係となります。
図を見るとNavigationController
もtabBarController
というプロパティを持っています。
これはUINavigationController
がUIViewController
を継承したクラスのためです。
同様にUITabBarController
もUIViewController
を継承しているためnavigationController
というプロパティを持ちます。
そのためさらにTabBarController
にNavigationController
を持たせることもできます。
この図をいますぐ理解できる必要はありませんが、実際にアプリを作る際には自分がどんな構成の画面遷移を実装しているのか図に起こせるように理解できるようにしましょう。
最後に
今回でiOSの画面遷移編は終了です。
モーダル、プッシュ、タブそれぞれの遷移に意味があり実装方法が多少異なります。
積極的に使って自分のものにしていきましょう。
最後の画面遷移の組み合わせはiOSを学ぶ上で躓く部分かもしれません。
これは画面遷移を実装→図に起こす→画面遷移を実装...と繰り返して感覚的に身につけていってください。
次回はライフサイクルという画面表示非表示になる際にコールされるイベントについて説明します。
https://qiita.com/euJcIKfcqwnzDui/items/d50dc4ec1ccc798fc03e
本連載ではプログラミング未経験からiOSアプリ開発が行えるようになることを目的としています。
今までの投稿をまとめていますのでこちらもご覧ください。
http://naoyalog.com/