【iOS】画面遷移方法まとめ

画面遷移の方法についてまとめます。

# プッシュ遷移とモーダル遷移
まずiOSアプリには、2種類の画面遷移方法があります。

- プッシュ遷移
- モーダル遷移

実装方法は後に書きますが、まずは両者の使い分けについて整理します。
 
## プッシュ遷移
雑に説明すると、普通の画面遷移のことです。
プッシュと呼ばれているのは、NavigationContollerが画面のスタックを持っていて、
そのスタックにpushしながら遷移するからだと思われます。
具体的なユースケースとしては、モーダル遷移にすべきとき以外全部という感じですが、挙げるとすれば下記でしょうか。

- NavigationBarの戻る・進むボタンで移動することが想定される画面

## モーダル遷移
「モーダル」（modal）という言葉をどう訳せばいいのか、色々調べてもわからなかったです。。。
よく下からニュッと出てくる画面、とか説明されます。
**単に、下から次の画面を出すのが目的ではありません。**

Modalityという単語の意味は、いくつか意味があるのですが、

> 2.（生理学）視覚、聴覚、触覚などの五感や感覚。また、それらを用いて外界を知覚する手段のこと。また、それらの感覚に働きかける人工的な情報伝達手段。
[モダリティ - ウィクショナリー日本語版](https://ja.wiktionary.org/wiki/%E3%83%A2%E3%83%80%E3%83%AA%E3%83%86%E3%82%A3)

多分ここでは「五感に訴えかける人工的な情報伝達手段」の意味なのだと思われます。
なんかいくつか検索して出てきた日本語記事見ましたが、上手い訳語をあててる例があまりないですね。。。
意味を考えると、この場では「応答待ち」とかがいいんだと思います。

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](https://developer.apple.com/design/human-interface-guidelines/ios/app-architecture/modality/)

つまり**ユーザーに絶対に入力させたい情報がある場合に、一時的に画面遷移を発生させる処理**がモーダル遷移です。
Human Interface Guidelinesでも書かれていますが、多用すべき方法ではありません。
基本的に何かメインの処理をしたいユーザーを、別の操作をさせて妨害することになるので、多用するとUXが低下するので、本当に必要なケース以外で使うべきではありません。
たとえば動画アプリを入れたのに、動画を再生するまでにアラート画面が何個も出て、そのたびに「OK」を押さなければいけないのはうっとうしいですよね？

具体例は下記のようなケースです。

- デバイスへのアクセス許可（カメラ、アルバム、位置情報など。これは最低限やらないと、App Storeに公開できません）
- ユーザーID/パスワードの入力画面
- テーブルビューの各アイテムへのアクセス（たとえばメーラーの個別のメール閲覧など）

# Interface Builderか、コードか
さて、具体的な実装方法についてまとめていきたいのですが、
単純な画面遷移であれば、Interface Builderを使って、StoryBoardを編集すれば、プログラミングなしで実装可能です。
たとえばユーザーが画面のボタンを押したら画面が動く処理であれば、GUIで簡単に作れます。
ただもっと複雑な条件をつけたいのであれば、コードを書く必要があります。

# Interface Builder
Interface Builderを使った方法を説明します。

## Interface Builderでプッシュ遷移する
ノンプログラミングで、画面遷移を実装する方法を書きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/333454/b5f1cf1f-ec31-e52d-32ad-c13742163163.png)
完成形としてはこんな感じです。

下記の手順でいけます。
>
1. 「Single Page Application」として新しいプロジェクトをつくる
2. 「Main.storyboard」を選択
3. 表示されているView Controllerを選択
4. 「Editor」→「Embed In」 →「Navigation Controller」を選択
5. 新たなView Controller（遷移先の画面）を追加する
6. 一枚目のView ControllerにButtonを追加 
7. Controlを押しながら、追加したButtonを二枚目の画面へドラッグアンドドロップ
※わかりやすいように、一枚目の背景を赤、二枚目の背景を青に変えています。

ここまでやると、下記のような表示が出ると思います。
ちなみに、この矢印をSegue（セグエ）と呼びます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/333454/4859b9a3-2aee-8f21-7a08-33b92bebaa25.png)
「Show」を選択すると、プッシュ遷移ができます。

シミュレータで起動すると、こんな画面です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/333454/59c87f43-765f-15e6-7b6b-7ec8459b019e.png)
Buttonを押してみましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/333454/871c82e2-a043-b6e6-04d2-247b7d2a2843.png)
二枚目に遷移しましたね。
Navigation BarのBackを押すと前の画面に戻れます。

## Interface Builderでモーダル遷移する
モーダル遷移も、プッシュ遷移と途中まで同じ方法で実装できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/333454/4859b9a3-2aee-8f21-7a08-33b92bebaa25.png)
このとき、「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」を選択してください。
すると２つのViewの間に矢印が出来たと思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/333454/d0c97b14-022a-18ce-4a5a-56ac233d1775.png)
この矢印がSegueをあらわしています。
Segueのidentifierに名前を入れましょう。
なんでもいいですが、ここでは例としてSegueNameと入れています。

そして、遷移元のViewController中で、画面遷移させたい場面に、下記のコードを入れてください。

```swift
//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に名前を入れてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/333454/0b29fc06-0142-2811-c569-62f7093fce82.png)
ここではnextViewとしました。
あとは遷移させたいところに、下記のコードを書いてください。

```swift
let storyboard: UIStoryboard = self.storyboard!
let nextView = storyboard.instantiateViewController(withIdentifier: "nextView")
navigationController?.pushViewController(nextView, animated: true)
```

## Segueを使わないモーダル遷移
遷移先のViewControllerのStoryboard IDに名前を入れるところまでは一緒です。

```swift
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やってます。エンジニアの知り合いが全然いないので、積極的につながりたいです！
よろしければフォローお願いします。

https://twitter.com/eg_19_