Edited at

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

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

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()
}
}



参考

https://medium.com/@stasost/ios-root-controller-navigation-3625eedbbff