自己紹介
はじめまして、ペンギン村で一番やかましい住人のナガクラ(@nagakuta)です!
Slackだけでなく、ブログもやかましく更新していきます!!(宣言)
TL;DR
RootViewControllerをAppDelegate.window.rootViewControllerに指定してから画面遷移するようにすると色々ラクだよ!Wireframeも一緒に使うとテスト書くときラクだよ!!
RootViewControllerによる画面遷移
RootViewController #とは
自分がRootViewControllerについて知ったのは、ペンギン村に貼られた以下の記事でした。
その記事のリンクにRootViewControllerについての詳細記事がありました。
その記事中にて説明されているRootViewControllerについて簡潔にザザッと説明すると、
AppDelegate.window.rootViewControllerに設定するViewControllerである- このViewControllerを起点に画面遷移を行うようにする
となります。なにこれ便利そう😲😲😲
そして、RootViewControllerを実装するメリットをサクッと説明すると、「1つのナビゲーションスタックだけで新しいViewControllerを表示したり、インタフェースなしで戻したりすることができる」、「なので、AppDelegate.window.rootViewControllerを差し替えるのがすごくラクになる」の2点でしょうか。なにそれすごく便利そう😂😂😂
RootViewControllerを実装してみる
では、実際にRootViewControllerを実装してみます( ´∀`)(一部端折っている箇所があります🙇)
RootViewController.swift
final internal class RootViewController: UIViewController {
// MARK: - Life Cycle Methods
init() {
super.init(nibName: nil, bundle: nil)
}
override viewDidLoad()
super.viewDidLoad()
// 起動直後に遷移させる画面を宣言
let launchViewController: UIViewController = LaunchViewController()
// 子ViewのViewControllerを指定
self.addChildViewContrlller(launchViewController)
// 子ViewをSubViewとして追加
launchViewController.view.frame = UIScreen.main.bounds
self.view.addSubView(launchViewController.view)
// 子Viewの所有権を譲渡
launchViewController.didMove(toParentViewController: self)
}
/// NextViewControllerに遷移
func transitToNextViewController() {
let nextViewController: UIViewController
// 子ビューのChildViewControllerを削除
let childViewController: UIViewController = self.childViewControllers.first!
childViewController.willMove(toParentViewController: nil)
childViewController.view.removeFromSuperView()
childViewController.removeFromParentViewController()
// 新しいViewControllerを子ビューとして追加
self.addChildViewController(nextViewController)
nextViewController.view.frame = UIScreen.main.bounds
self.view.addSubView(nextViewController.view)
nextViewController.didMove(toParentViewController: self)
}
}
LaunchViewController.swift
final internal class LaunchViewController: UIViewController {
// MARK: - Life Cycle Methods
(中略)
override viewDidAppear(_ animated: Bool) {
let appDelegate: AppDelegate = UIApplication.shared.delegate as! AppDelegate
let rootViewController: UIViewController = appDelegate.window!.rootViewController as! RootViewController
// NextViewControllerに遷移
rootViewController.transitToNextViewController()
}
(以下略)
}
AppDelegate.swift
@UIApplicationMain
internal class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// 起動直後に遷移する画面をRootViewControllerに指定する
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window.rootViewController = RootViewController()
self.window.makeKeyAndVisible()
return true
}
(以下略)
}
これで、起動 → RootViewControllerに遷移 → LaunchViewControllerに遷移 → NextViewControllerに遷移が実現できました( ´∀`)bグッ!
ここからChildViewControllerでLaunchScreenとするなり、ログイン画面とするのが一般的なiOSアプリケーションの起動直後の動きになると思います。
Wireframeで画面遷移
Wireframe #とは
Wireframeというのをざっくりと説明すると、「画面遷移をViewControllerの代わりに行うための仕組み」です。
通常、画面遷移を実装する際はUIViewControllerのメソッドを利用して行うと思います。(先程のRootViewControllerでも、Viewの上に重ねるという形で画面遷移を実装しています)
しかし、画面遷移をUIViewControllerクラスで行うと、少しViewControllerが膨らんでしまいますし、ViewControllerごとに同様の処理を何回も何回も実装するのはアホくs…スマートじゃないですよね😅
そんな時に使ってみたいのがWireframeです。Wireframeで画面遷移用のメソッドを実装し、ViewControllerクラスからそのメソッドを呼び出すようにすれば、いちいち画面遷移についてあーだこーだと考えずにViewControllerは画面表示のことに専念できます👍
また、遷移先の画面を指定できるようになれば、Unitテストの際にモックのViewControllerを指定することができるようになるため、テストを書くのが格段に楽チンになります😆😆😆
Wireframeによる画面遷移を実装してみる
ではでは、Wireframeを実装してみましょう(`・ω・´)ガンバル
RootWireframe.swift
internal protocol Wireframe {
/// 指定した画面に遷移
func transition(to viewController: UIViewController)
}
internal struct RootViewWireframe: Wireframe {
func transition(to viewController: UIViewController) {
let appDelegate: AppDelegate = UIApplication.shared.delegate as! AppDelegate
let rootViewController: RootViewController = appDelegate.window!.rootViewController as! RootViewController
// RootViewControllerの子ビューを削除
if !rootViewController.childViewControllers.isEmpty {
rootViewController.childViewControllers.forEach { (childViewController: UIViewController) in
childViewController.willMove(toParentViewController: nil)
childViewController.view.removeFromSuperView()
childViewController.removeFromParentViewController()
}
// 以下はRootViewController.viewDidLoadメソッドの内容をそのまま移植
rootViewController.addChildViewController(viewController)
viewController.view.frame = UIScreen.main.bounds
rootViewController.view.addSubView(viewController.view)
viewController.didMove(toParentViewController: rootView)
}
}
これでWireframeの実装ができました👍
では、RootViewControllerの画面遷移をWireframeに移譲します(`・ω・´)シャキーン
RootViewController.swift
final internal class RootViewController: UIViewController {
let wireframe: RootViewWireframe = RootViewWireframe()
(中略)
override viewDidLoad() {
super.viewDidLoad()
// Wireframeによる画面遷移
let childViewController: UIViewController = UIViewController()
self.wireframe.transition(to childViewController)
}
(以下略)
}
LaunchViewController.swift
final internal class LaunchViewController: UIViewController {
let wireframe: RootViewWireframe = RootViewWireframe()
(中略)
override func viewDidAppear(_ animated: Bool) {
let nextViewController: UIViewController = UIViewController()
self.wireframe.transition(to nextViewController)
}
(以下略)
}
これで、Wireframeによる画面遷移が実装できました!(∩´∀`)∩ワーイ
これで、AppDelegate.window.rootViewControllerの差し替えも容易になり、重なりまくった子ビューから最下層のViewを取得するのもRootViewController.childViewControllers.firstで取得できるようになります💪💪💪
あとがき
「RootViewController + Wireframe による画面遷移」、いかがだったでしょうか😃😃😃
これからも、「楽に、最小効率で」を目的に勉強したTipsをこのブログで公開していきたいなと思います(`・ω・´)
次回更新もお楽しみに!!!