[Swift 5]RootViewControllerを適用する備忘録

最終更新日 投稿日 2019年08月10日

例えば、初回起動時の利用規約確認ページの表示やウォークスルーの表示、またそれらの画面からメイン画面への移動について

AppDelegateを介してwindowrootViewControllerを切り替えたりしていたのですが、
メモリの関係であまり推奨されないらしい。

今回は専用のRootViewControllerを用意し、そのchildを挿げ替えるという方法があると知り、実践しました。
あわせて、起動時にはSplashViewControllerを用意して何らかのデータ取得や表示先画面の分岐を行っています。
今後Splash画面をアニメーションさせたくなったときにも入れやすそうです。

前提情報

個人的な好みですが、随所でViewControllerVCと略しています。

まずは肝心のRootViewControllerの作成

RootVC.swift
                            
                                import UIKit
                                final class RootVC: UIViewController {
                                    // 現在表示しているViewControllerを示します
                                    private var current: UIViewController
                                    init() {
                                        // 起動時最初の画面はSplashViewControllerを設定します
                                        current = SplashVC()
                                        super.init(nibName: nil, bundle: nil)
                                    }
                                    // init()を実装したことによる必須実装
                                    required init?(coder aDecoder: NSCoder) {
                                        fatalError("init(coder:) has not been implemented")
                                    }
                                    override func viewDidLoad() {
                                        super.viewDidLoad()
                                        // ViewControllerをRootVCの子VCとして追加
                                        addChild(current)
                                        current.view.frame = view.bounds
                                        view.addSubview(current.view)
                                        current.didMove(toParent: self)
                                    }
                                    /// RootVCの子VCを入れ替える=ルートの画面を切り替える
                                    func transition(to vc: UIViewController) {
                                        // 新しい子VCを追加
                                        addChild(vc)
                                        vc.view.frame = view.bounds
                                        view.addSubview(vc.view)
                                        vc.didMove(toParent: self)
                                        // 現在のVCを削除する準備
                                        current.willMove(toParent: nil)
                                        // Superviewから現在のViewを削除
                                        current.view.removeFromSuperview()
                                        // RootVCから現在のVCを削除
                                        current.removeFromParent()
                                        // 現在のVCを更新
                                        current = vc
                                    }
                                    // 移動したいViewControllerごとに用意しておくと簡単に使用できる
                                    func transitionToMain() {
                                        // 切り替えたい先のViewControllerを用意
                                        let vc = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController()!
                                        transition(to: vc)
                                    }
                                }
                            
                        

AppDelegateで、最初に表示する画面をRootVCに設定する

AppDelegate.swift
                            
                                class AppDelegate: UIResponder, UIApplicationDelegate {
                                    var window: UIWindow?
                                    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
                                        // 以下を追加
                                        // 最初に表示させるViewControllerにRootVCを指定する
                                        window = UIWindow(frame: UIScreen.main.bounds)
                                        window!.rootViewController = RootVC()
                                        window!.makeKeyAndVisible()
                                        return true
                                    }
                                    // その他メソッド省略…
                                }
                                extension AppDelegate {
                                    /// AppDelegateのシングルトン
                                    static var shared: AppDelegate {
                                        return UIApplication.shared.delegate as! AppDelegate
                                    }
                                    /// rootViewControllerは常にRootVC
                                    var rootVC: RootVC {
                                        return window!.rootViewController as! RootVC
                                    }
                                }
                            
                        

SplashVC

SplashVC.swift
                            
                                import UIKit
                                /// 起動時、LauchScreenの後に表示される最初の画面。起動処理や遷移先画面の分岐を行う
                                final class SplashVC: UIViewController {
                                    /// 処理中を示すインジケーター
                                    private lazy var activityIndicator: UIActivityIndicatorView = {
                                        let indicator = UIActivityIndicatorView(style: .whiteLarge)
                                        indicator.frame = view.bounds
                                        indicator.backgroundColor = UIColor(white: 0, alpha: 0.4)
                                        return indicator
                                    }()
                                    /// 処理中に表示させておくスプラッシュ画像。LaunchScreenでも同じ画像を指定しておく
                                    private lazy var splashImage: UIImageView = {
                                        let imageView = UIImageView(
                                            image: UIImage(named: "Japanese01")
                                        )
                                        imageView.contentMode = .scaleAspectFill
                                        imageView.frame = view.frame
                                        return imageView
                                    }()
                                    override func viewDidLoad() {
                                        super.viewDidLoad()
                                        view.addSubview(splashImage)
                                        view.addSubview(activityIndicator)
                                        // 起動時に行いたい処理を実行する
                                        // UsecaseなりPresenterなり・・
                                        activityIndicator.startAnimating()
                                        // 中略
                                        // メイン画面へ移動
                                        AppDelegate.shared.rootVC.transitionToMain()
                                    }
                                }
                            
                        

参考