windowのrootViewControllerを変更して画面遷移する方法

新しい画面遷移の方法を学んだから半分メモ書き感覚で書いていくで〜
訂正、編集リクエスト、感激やで〜。

# 目的
作成したいものは以下のように、ボタンを押したら画面遷移する機能。
<img width=30% src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/707293/05a0791c-4e25-03c0-ecab-14589ce5ff05.gif">

これを、Segue 等を使わないやり方で画面遷移させる。
要は、RootViewController を使ってアニメーションで画面遷移をさせる。

# 環境
- macOS Monterey: Version12.4
- Xcode: Version 13.4.1
- Swift: Version 5.6.1

# 前提
[コチラの記事](https://zenn.dev/nkysyuichi/articles/c3c2a6a63e1404)を参考に以下のようなことを行います。
1. AppDelegate から `// MARK: UISceneSession Lifecycle` 以降のコードを削除する
1. AppDelegate に window プロパティを追加
1. Info.plist から UIApplicationSceneManifest を全て削除
1. SceneDelegate.swift を削除する

正直、削除するかは任意だが、2 の `window` プロパティだけは追加していて欲しい。

なので、`AppDelegate.swift` は、以下のようになっている。

```swift:AppDelegate.swift
import UIKit

@main
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        return true
    }
}

```

# Storyboardでの設定
ViewController を追加していく。今回はわかりやすいように、青、黄、赤の画面をそれぞれ、`FirstViewController`, `SecondViewController`, `ThirdViewController` とした。
![スクリーンショット 2022-06-16 22.29.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/707293/40647eee-c19f-39d0-dc90-3ec0c51bebc1.png)

次に、ボタンを押すと画面遷移するようにボタンを配置していく。
配置したものがコチラ↓
![スクリーンショット.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/707293/8067ec2f-67fe-739c-d439-7896dd09cdad.png)
最後に、`Storyboard ID` を設定する。
今回は、`FirstViewController`, `SecondViewController`, `ThirdViewController` です。
そのため、順番に `FirstVC`, `SecondVC`, `ThirdVC` とする。
<img width = 40% src = "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/707293/faf9cd34-540a-874e-a395-66d4d4f50527.png">
これで準備は OK。特に `Storyboard ID` を設定は忘れないように。

# AppDelegateでの設定

```diff_swift: AppDelegate.swift
import UIKit

@main
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?
+    let storyboard = UIStoryboard(name: "Main", bundle: nil)

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
+        self.window = UIWindow(frame: UIScreen.main.bounds)
+        self.window?.makeKeyAndVisible()
+        let initialViewController = self.storyboard.instantiateViewController(withIdentifier: "FirstVC")
+        self.window?.rootViewController = initialViewController

        return true
    }

+    func switchViewController(identifier: String) {
+        UIView.transition(with: self.window!, duration: 0.5, options: .transitionCrossDissolve, animations: {
+            let oldState: Bool = UIView.areAnimationsEnabled
+            UIView.setAnimationsEnabled(false)
+            self.window?.rootViewController = self.storyboard.instantiateViewController(withIdentifier: identifier)
+            UIView.setAnimationsEnabled(oldState)
+        }, completion: nil)
+    }
}
```

# ViewControllerでの設定
UI パーツとコードを紐付けすると、↓のようになる。
```diff_swift:　FirstViewController
import UIKit

final class FirstViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
    }

+   @IBAction private func goToSecondVCButton(_ sender: Any) {
+   }

}
```
メソッドの命名はご愛嬌ということで💦。

次に、画面遷移をさせるために、`func goToSecondVCButton` に先ほど作成した `func switchViewController(identifier: String)` を呼ばせる。

そうすると、↓のようになる。
```diff_swift: FirstViewController
import UIKit

final class FirstViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
    }

    @IBAction private func goToSecondVCButton(_ sender: Any) {
+        guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
+        appDelegate.switchViewController(identifier: "SecondVC")
    }
}
```
同様のことを `SecondViewController`, `ThirdViewController` にも行う。
```diff_swift: SecondViewController
import UIKit

final class SecondViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
    }

+    @IBAction private func goToThirdVCButton(_ sender: Any) {
+        guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
+        appDelegate.switchViewController(identifier: "ThirdVC")
+    }
}
```

```diff_swift: ThirdViewController
import UIKit

final class ThirdViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
    }

+    @IBAction private func goToFirstVCButton(_ sender: Any) {
+        guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
+        appDelegate.switchViewController(identifier: "FirstVC")
+    }
}
```
これで、Segue を使わずに RootViewController をアニメーションで画面遷移できた。

# 参考文献

https://en.swiswiswift.com/2019-02-01/

https://qiita.com/tomu28/items/4bb327a8f80c042e41a1