画面遷移の方法についてまとめます。
プッシュ遷移とモーダル遷移
まずiOSアプリには、2種類の画面遷移方法があります。
- プッシュ遷移
- モーダル遷移
実装方法は後に書きますが、まずは両者の使い分けについて整理します。
プッシュ遷移
雑に説明すると、普通の画面遷移のことです。
プッシュと呼ばれているのは、NavigationContollerが画面のスタックを持っていて、
そのスタックにpushしながら遷移するからだと思われます。
具体的なユースケースとしては、モーダル遷移にすべきとき以外全部という感じですが、挙げるとすれば下記でしょうか。
- NavigationBarの戻る・進むボタンで移動することが想定される画面
モーダル遷移
「モーダル」(modal)という言葉をどう訳せばいいのか、色々調べてもわからなかったです。。。
よく下からニュッと出てくる画面、とか説明されます。
単に、下から次の画面を出すのが目的ではありません。
Modalityという単語の意味は、いくつか意味があるのですが、
2.(生理学)視覚、聴覚、触覚などの五感や感覚。また、それらを用いて外界を知覚する手段のこと。また、それらの感覚に働きかける人工的な情報伝達手段。
モダリティ - ウィクショナリー日本語版
多分ここでは「五感に訴えかける人工的な情報伝達手段」の意味なのだと思われます。
なんかいくつか検索して出てきた日本語記事見ましたが、上手い訳語をあててる例があまりないですね。。。
意味を考えると、この場では「応答待ち」とかがいいんだと思います。
Human Interface Guidelinesから該当箇所を丸々引用すると、
(拙訳)
Modality(応答待ち)は、ユーザーがタスクを完了させるか、メッセージorビューを却下するまで、他の動作をさせないためのものです。アクションシート(?)、アラート、アクションビュー(?)で応答待ち処理を実装できます。応答待ちのビューが画面に表示されたら、ユーザーはボタンをタップするか、応答待ち処理から抜けるかを選ばなければいけません。
Modality creates focus by preventing people from doing other things until they complete a task or dismiss a message or view. Action sheets, alerts, and activity views provide modal experiences. When a modal view appears onscreen, the user must make a choice by tapping a button or otherwise exiting the modal experience.
Human Interface Guidelines
つまりユーザーに絶対に入力させたい情報がある場合に、一時的に画面遷移を発生させる処理がモーダル遷移です。
Human Interface Guidelinesでも書かれていますが、多用すべき方法ではありません。
基本的に何かメインの処理をしたいユーザーを、別の操作をさせて妨害することになるので、多用するとUXが低下するので、本当に必要なケース以外で使うべきではありません。
たとえば動画アプリを入れたのに、動画を再生するまでにアラート画面が何個も出て、そのたびに「OK」を押さなければいけないのはうっとうしいですよね?
具体例は下記のようなケースです。
- デバイスへのアクセス許可(カメラ、アルバム、位置情報など。これは最低限やらないと、App Storeに公開できません)
- ユーザーID/パスワードの入力画面
- テーブルビューの各アイテムへのアクセス(たとえばメーラーの個別のメール閲覧など)
Interface Builderか、コードか
さて、具体的な実装方法についてまとめていきたいのですが、
単純な画面遷移であれば、Interface Builderを使って、StoryBoardを編集すれば、プログラミングなしで実装可能です。
たとえばユーザーが画面のボタンを押したら画面が動く処理であれば、GUIで簡単に作れます。
ただもっと複雑な条件をつけたいのであれば、コードを書く必要があります。
Interface Builder
Interface Builderを使った方法を説明します。
Interface Builderでプッシュ遷移する
ノンプログラミングで、画面遷移を実装する方法を書きます。
完成形としてはこんな感じです。
下記の手順でいけます。
- 「Single Page Application」として新しいプロジェクトをつくる
- 「Main.storyboard」を選択
- 表示されているView Controllerを選択
- 「Editor」→「Embed In」 →「Navigation Controller」を選択
- 新たなView Controller(遷移先の画面)を追加する
- 一枚目のView ControllerにButtonを追加
- Controlを押しながら、追加したButtonを二枚目の画面へドラッグアンドドロップ
※わかりやすいように、一枚目の背景を赤、二枚目の背景を青に変えています。
ここまでやると、下記のような表示が出ると思います。
ちなみに、この矢印をSegue(セグエ)と呼びます。
シミュレータで起動すると、こんな画面です。
Buttonを押してみましょう。
二枚目に遷移しましたね。
Navigation BarのBackを押すと前の画面に戻れます。
Interface Builderでモーダル遷移する
モーダル遷移も、プッシュ遷移と途中まで同じ方法で実装できます。
このとき、「Present Modally」を選択しましょう。
モーダル遷移に設定できます。
モーダルで遷移させた際は、Navigation Barが表示されません。
ここがプッシュ遷移との大きな違いです。
現在説明しているアプリの例だと、モーダル遷移にすると戻れなくなります。
モーダル遷移先の画面を閉じる処理は、何かしら開発者が実装する必要があります。
用語の整理
Interface Builderで画面と画面を結んでいる矢印をSegue(セグエ)と呼びます。
また、画面をSceneと呼びます。
Storyboard上で、SceneとSceneを結ぶのがSegueです。
コード
コードで画面遷移します。
Interface Builderは細かいUIを直感的に調整できるので、基本的には使うべきです。
ただ、エンジニアからすると、コードの外でUIの設定を持つので、ちょっと扱いづらいところがあるので、
原理主義的な人だと、Interface Builderは一切使わず、すべてコードでやってしまいます。
それはちょっと過激派ですが、Interface Builderでどこまでやるかはチームによるみたいです。
コードで画面遷移を実装する場合、Segueを使うか、使わないかが焦点になると思います。
やり方としては、どっちでもできます。
Segueを使ったプッシュ遷移
厳密に言うと、最初に紹介したInterface Builderを使って遷移する方法もSegueを使ったものです。
ただそのSegueの発動ポイントを、コード中に書くか書かないかの違いです。
Interface Builder上で、遷移元のViewControllerをControlを押しながらドラッグして遷移先のViewまでドロップしてください。
ドロップする際に、先程と同様に「Show」とか「Present Modally」とかを選ぶことになると思いますので、「Show」を選択してください。
すると2つのViewの間に矢印が出来たと思います。
この矢印がSegueをあらわしています。
Segueのidentifierに名前を入れましょう。
なんでもいいですが、ここでは例としてSegueNameと入れています。
そして、遷移元のViewController中で、画面遷移させたい場面に、下記のコードを入れてください。
//SegueNameはInterfaceBuilder上で自分がつけた名前にしてね
performSegue(withIdentifier: "SegueName", sender: nil)
senderに変数を指定すると、値が受け渡せるので、全部Interface Buiderで行った例よりも、動作の幅が広がります。
ググると、selfをつけている例もありますが、ViewController内であれば、省略可能だと思います。
Segueを使ったモーダル遷移
Interface Builder上でSegueを作成するとき、「Show」ではなく、「Present Modally」を選んでください。
あとはプッシュ遷移と同じです。
Segueを使わないプッシュ遷移
Segueを使わないでコーディングすると、どうなるのでしょうか。
ここでは、Storyboardをコードで呼び出して、そこから遷移先のViewControllerを指定し、プッシュ遷移する方法を紹介します。
まず遷移先のViewControllerのStoryboard IDに名前を入れてください。
ここではnextViewとしました。
あとは遷移させたいところに、下記のコードを書いてください。
let storyboard: UIStoryboard = self.storyboard!
let nextView = storyboard.instantiateViewController(withIdentifier: "nextView")
navigationController?.pushViewController(nextView, animated: true)
Segueを使わないモーダル遷移
遷移先のViewControllerのStoryboard IDに名前を入れるところまでは一緒です。
let storyboard: UIStoryboard = self.storyboard!
let nextView = storyboard.instantiateViewController(withIdentifier: "nextView")
//ここがpushとは違う
self.present(nextView, animated: true, completion: nil)
戻りたいときはdismissを使います。
ちなみに
遷移先のView / ViewControllerの生成とかやろうと思えばコードで記述できるので、Interface Builderをまったく使わないで画面遷移を書くこともできます。
まあでも、UIを細かく調整するのであれば、素直にInterface Builder使うが吉と思われます。
おまけ
Twitterやってます。エンジニアの知り合いが全然いないので、積極的につながりたいです!
よろしければフォローお願いします。