LoginSignup
4
6

More than 1 year has passed since last update.

LaunchScreenを表示したままにしてViewControllerを動かす

Last updated at Posted at 2019-09-16

やりたいこと

S12019-09-17 6.11.02.png

LaunchScreenを表示した後、画面はそのままで初期化用のStartupViewControllerを起動します。
初期化終了後にMainViewControllerを起動します。

StartupViewControllerを起動する

SceneDelegateのscene(_: willConnectTo:options)でStartupViewControllerをrootViewControllerにセットします。

SceneDelegate.swift
// root view controller
let viewController = StartupViewController()
window.rootViewController = viewController
window.makeKeyAndVisible()

LaunchScreenのStoryboard名をInfo.plistから取得する

ハードコードで"LaunchScreen"でもいいんですけど。:relaxed:

InfoPlistUtil.swift
struct InfoPlistUtil {

    private init() {} // prevent instantiation.

    static func objectForKey<T>(_ key: String) -> T? {
        Bundle.main.object(forInfoDictionaryKey: key) as? T
    }

    static func stringForKey(_ key: String) -> String? {
        objectForKey(key)
    }
        
    static var launchStoryboardName: String? {
        stringForKey("UILaunchStoryboardName")
    }
}

LaunchScreenを取得する

LaunchScreenをViewControllerごとインスタンス化してそのviewを取得します。

ViewUtil.swift
struct ViewUtil {

    private init() {} // prevent instantiation.

    static var launchScreen: UIView? {
        guard let name = InfoPlistUtil.launchStoryboardName else {
            return nil
        }
        let storyboard = UIStoryboard(name: name, bundle: nil)
        let viewController = storyboard.instantiateInitialViewController()
        let retainedView = viewController?.view // retain Launch Screen
        viewController?.view = nil // Cut reference from viewController to Launch Screen
        return retainedView
    }
}

肝なところなので解説もご覧ください。

StartupViewControllerにLaunchScreenをセットする

StartupViewControllerのviewにLaunchScreenをセットします。

StartupViewController.swift
override func loadView() {
    super.loadView()

    if let launchScreen = ViewUtil.launchScreen {
        view = launchScreen
    }
}

MainViewControllerを起動する

初期化終了後にMainViewControllerをインスタンス化してrootViewControllerにセットします。

このまま実行できませんが雰囲気はわかりますよね?ベタで書くの面倒だったので。:relaxed:

StartupViewController.swift
// 初期化終了後
let viewController = ViewUtil.retrieveViewController(MainViewController.self)
RootViewControllerSegue(source: self, destination: viewController)
    .perform()

解説

ダメパターン(1)

001.png
instantiateInitialViewController()を実行した直後はLaunchScreenが無名ViewControllerに割り当てられています。
この状態でLaunchScreenをStartupViewControllerに割り当てるとクラッシュします。
(1つのUIViewを複数のUIViewControllerに割り当てできないため)

ダメパターン(2)

002.png
無名ViewControllerからLaunchScreenへの参照を切ってみたらどうでしょうか?
LaunchScreenの参照数が0になってしまうので、LaunchScreenがメモリから解放されてしまいます。
(詳細はARCの仕組みを調べてみてください)

成功パターン

003.png

  1. 変数retainedViewを用意してLaunchScreenを参照することで参照数が0になることを防ぎます。
  2. 無名ViewControllerからLaunchScreenへの参照を切ります。
  3. LaunchScreenをStartupViewControllerに割り当てます。

ソースコード

GitHubにソースコード一式を置きましたの参考にしていただければ幸いです。
hackenbacker/LaunchScreenSample


動作環境

Xcode13.4.1 + iOS13.0 or later

4
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
6