【iOS】遷移方法まとめとエンジニア1年生の独り言

# iOS遷移方法まとめ

案件にて、主に Navigation Controller に関する懸念点を考慮し Controller の構成を考える必要があったため、今回は iOSにおける一般的な遷移方法をまとめてみました。

遷移方法・Controller の hierarchy を決定するために役立てられればいいなと思っています。

また、最後に[エンジニア1年生の独り言](#エンジニア1年生の独り言)も記載しています。

ご指摘・ご感想・たわいもないコメントなどいただければ嬉しいです！

### 環境

- Xcode 11.2.1
- Swift 5.0
- iPhone 11 Pro Max (iOS13.2.2, シミュレータ)

# 遷移の種類

一般的な遷移の種類は大きく分けて以下の4つです。

- push遷移
- modal遷移: [番外編: モーダルとは?](#番外編-モーダルとは)
- tab切り替え
- page遷移

||push遷移|modal遷移|tab切り替え|page遷移|
|---|---|---|---|---|
|遷移時のアクション|指定なし|指定なし|タブアイテムをタップ|左右スワイプ|
|遷移先の一般的なデザイン|UINavigationBar左部分に「< 戻る」<br>or<br>「< (遷移元のtitle)」|画面左上に「キャンセル」<br>or<br>「閉じる」ボタンを配置|選択中のタブアイテムがTint Colorで色付けされる|-|
|戻る方法|「< 戻る」ボタンタップ<br>or<br>右スワイプ|画面を破棄する処理を実行<br>or<br>下スワイプ(iOS13~)|-|-|
|一般的なアニメーション|右から画面が出現|下から画面が出現|瞬時に切り替わる|左右から画面が出現|
|GIF|![push.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/296819/f01d43bf-8178-7584-6873-da132ff63ba2.gif)|![modal.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/296819/357142cb-ac12-2b52-20bf-a332e2fde168.gif)|![tabbar.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/296819/237461bd-57af-ce56-7cec-f5431235327d.gif)|![page.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/296819/00359e1f-72fa-a5b7-75dc-60c945fa7a1b.gif)|

# 遷移を実行するController

前項の各遷移を実行するのは以下のような Controller たちです。

- UIViewController: 以下すべての Controller の親
- UINavigationController
- UITabBarController
- UIPageViewController

すべての Controller がすべての遷移を行えるわけではないので、次に遷移実行の可否を表にまとめます

### 遷移実行の可否

|遷移を実行するController|push遷移|modal遷移|tab切り替え|page遷移|
|---|---|---|---|---|
|UIViewController|×|○|×|×|
|UINavigationController|○|○|×|×|
|UITabBarController|×|○|○|×|
|UIPageViewController|×|○|×|○|

# 遷移実行の方法
|遷移を実行するController|push遷移|modal遷移|tab切り替え|page遷移|
|---|---|---|---|---|
|UIViewController|×|vc.<font color="DarkGreen">present(_ viewControllerToPresent:, animated:)</font><br> ↑ UIViewControllerを継承した Controller (以下の3つ含む)で使用可能|×|×|
|UINavigationController|nc.pushViewController(_ viewController:, animated:)<br>or<br>nc.show(_ vc:, sender:)|nc.<font color="DarkGreen">present(_ viewControllerToPresent:, animated:)</font><br>or<br>nc.showDetail(_ vc:, sender:)|×|×|
|UITabBarController|×|tc.<font color="DarkGreen">present(_ viewControllerToPresent:, animated:)</font>|UITabBarController に UITabBar, UITabBarItem, 対象の View Controller を設定する。遷移時は UITabBarItem をタップ。|×|
|UIPageViewController|×|pc.<font color="DarkGreen">present(_ viewControllerToPresent:, animated:)</font>|×|UIPageViewController に View Controller を設定する。遷移時はスワイプ。|


## segue

遷移の実装方法として、`segue`を使用する方法もあります。

簡単な手順

1. storyboard 上でView Controller or Storyboard Reference を配置
2. 親のView Controllerと接続する: この接続が`segue`の本体
2. `segue` の identifier を設定
3. storyboard 上でなんらかの Action と結びつける<br>or<br>コードから UIViewController 内で performSegue(withIdentifier:, sender:)を呼ぶ。<br>（このメソッドは`UIViewController`で定義されているので）

＊ push遷移は、segue が定義された View Controller が親に UINavigationController を持っていないと実行できず、UINavigationController を持たない場合は storyboard で push遷移を設定していたとしても modal遷移する(iOS13で確認)。

## ライブラリ

また、ライブラリを使用する手段もあります。

ここでは、標準の機能では実装が難しいような遷移を実装するためのライブラリを少しだけですが添付しておきます。

- [XLPagerTabStrip](https://github.com/xmartlabs/XLPagerTabStrip)：画面上部にタブバーを設定する
- [FloatingPanel](https://github.com/SCENEE/FloatingPanel)：モーダルビュー、セミモーダルビューを実装できる。iOS13より前バージョンでも。

# エンジニア1年生の独り言

最後に、独り言をつらつらと書きたいと思います。

少し長いので、読み物として読んでいただければと思います。

最近は[Human Interface Guidelines](https://developer.apple.com/design/human-interface-guidelines/) などを見ながら、各コンポーネントは何のために作られたのか、どのような特徴を持っているのかを学習するのが楽しいです。
それらを意識して実装するのが奥深くて面白いなと未熟ながら感じています。

例えば、今回の「遷移」について言えば、モーダル遷移に関して色々と考えを巡らせることができました。

## 番外編: モーダルとは?

モーダル遷移のモーダルは英語で書くと mode の形容詞形である `modal` なので、モーダル遷移はつまり〇〇モードを実行するための遷移です。

アクションシートなどのアラートも一種のモーダルな遷移・デザイン手法で、ユーザーの注意を引きそのときに必要な情報を提供したり、必要なアクションを起こさせたりすることができます。

モードに入っているときは他の作業は行えません。

遷移先の画面では 通常 Navigation Bar は存在せず、前の画面に`戻る`ためのボタンもなく、配置されるのは一般的に`キャンセル`や`閉じる`ボタンです。

モーダル遷移後の処理は独立していて前画面とは切り離されている証拠です。

そう考えると、アクションシートや（セミ）モーダルビュー表示時にそれ以外の部分を薄暗くしタップできなくなるようにした方が良いという考えにも納得できます。

このように考えを巡らせ論理的に理解することで、綺麗にそしてユーザーに優しく実装することができるのではないかと考えています。

## 色々と実装をデザインするのは難しい！と同時に面白い

この記事を書いた動機は、あるデザイン要件を満たすためにはどのような Controller を、どの階層に配置し、どのような手段を選べば、期待通りの遷移を実装できるかを考えやすくしようと思ったからです。

そこで、[Human Interface Guidelines](https://developer.apple.com/design/human-interface-guidelines/) を読んだり、既存の有名なアプリをよく観察したりしました。
しかし、今の自分にとっては既存のアプリがどのように実装されているのかを判断することは少し難しく感じました。
（色々と考え、真似してみることは勉強になるとは思っています！）

遷移に関して言えば、様々な Controller を共存すること、画面ごとに異なるデザインの Navigation Bar を構築することが難しかったです。

画面ごと、遷移ごとに以下のような問題について考える必要があり、さらには滑らかなアニメーションで遷移することが理想とされるからです。

- Tab Bar の表示/非表示
- Tab Bar のデザイン
- Navigation Bar の表示/非表示
- Navigation Bar のデザイン
- （セミ）モーダルビューを最前面に表示する

特に、Navigation Bar のデザインが統一されていない場合、push遷移時に Navigation Bar の背景色を滑らかに切り替えるのは難しいです。

それを実現するための1つの案は、Navigation Bar ごとフルスクリーンで遷移することです(伝わりますでしょうか...)。そのためには、UINavigationBarを使用せず自作 Navigation Bar なるものを作成する他ないと思われます。

もう1つの案は、遷移時のアニメーションを実装することです。そのためには、ぼかし具合などを計算し自力でコードを書く(or 何らかのライブラリを使用する)しかありません。

シンプル・イズ・ザ・ベストな考え方が好きなので、基本的に標準でできないこと推奨されないことは無理に実装したくありませんが、デザインがそうなっていれば仕方ありません。

各コンポーネントの用途を理解してスッキリ実装したものが、開発者にもユーザーにも分かりやすくて『良い』と私は思いますが、『良い』という言葉は主観量なのでその人、その企業にとっての『良い』デザインは異なるんだろうなぁと思ってます。。

なので、仕方ないですね。。

やっぱり自分にはまだまだ難しいです。。

が、絵的な意味のデザインを実装するための手段をデザインすることは奥深く面白いなぁと感じています!

今後も、[Human Interface Guidelines](https://developer.apple.com/design/human-interface-guidelines/) やデザインの実装方法に関する記事を読むこと、既存アプリやUI構築方法の考察などは続けていきたいです。

# 最後に

今回は、ごくごく簡単に実装しながら遷移方法をまとめてみました。

遷移に限った話ではありませんが、様々な方法の中からベストな方法を抽出したり組み合わせたりできるよう精進します！

### 参考
- [Apple Developer Documentation](https://developer.apple.com/documentation/)
- [Human Interface Guidelines](https://developer.apple.com/design/human-interface-guidelines/)