Help us understand the problem. What is going on with this article?

【Xcode11】Storyboardを使わずコードだけで画面を生成する方法

目的

Xcode11でStoryboardを使わずにコードだけで画面を作ろうと思ったときにCould not find a storyboard named 'Main' in bundle NSBundleというエラーがずっと出ていてだいぶ困ったので忘れないように残しておこうと思います。
気が付いてみたらだいぶ簡単なエラーだったのですが、もし同じところで行き詰まっている人がいれば参考にしてください!笑

Storyboardを使わずに画面を生成する方法(Xcode11より前)

Xcode11より前のStoryboardを使わずにコードだけで画面を生成するときには、
1.Main.storyboardを削除。
2.プロジェクトのTARGETS > InfoにあるMain storyboard file base nameという項目を削除。
スクリーンショット 2019-10-22 0.27.08.png

3.AppDelegate.swiftで初回の起動画面を設定。

AppDelegate.swift
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.

        window = UIWindow(frame: UIScreen.main.bounds)
        window?.rootViewController = ViewController()
        window?.makeKeyAndVisible()

        return true
    }
}

これで初回起動時に表示される画面がViewController.swiftになると思います。

Storyboardを使わずに画面を生成する方法(Xcode11の場合)

Xcode11でプロジェクトを作成するとAppDelegate.swiftだけではなくSceneDelegate.swiftというものも一緒に生成されます。
Xcode11ではSceneDelegate.swiftでもStoryboardを使わないように設定しないといけません。
1.Main.storyboardを削除。
2.プロジェクトのTARGETS > InfoにあるMain storyboard file base nameという項目を削除。
3.プロジェクトのTARGETS > InfoにあるApplication Scene Manifest > Scene Configuration > Application Session Role > Item0(Default Configura... > Storyboard Nameを削除。(私はここでもMain.storyboardを指定していることに気がつかずにずっとエラーが出ていました!笑)
スクリーンショット 2019-10-22 0.44.40.png
4.AppDelegate.swiftで初回の起動画面を設定。(Xcode11より前と同じ)

5.SceneDelegate.swiftで初回の起動画面を設定。

SceneDelegate.swift
class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow?

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
        // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
        // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
        guard let scene = (scene as? UIWindowScene) else { return }
        window = UIWindow(windowScene: scene)
        window?.rootViewController = ViewController()
        window?.makeKeyAndVisible()
    }
}

これでXcode11でもStoryboardを使わずに画面を生成することができます。

iOS13以前のバージョンにも対応させる方法

iOS13以降だけでなく古いバージョンのOSにも対応させた方がいいと思うので、iOS13以前のバージョンにも対応させる方法です。
簡単にいうと、iOS13以降ではアプリ起動時にSceneDelegate.swiftが呼ばれて、iOS13以前ではAppDelegate.swiftが呼ばれるみたいなので各メソッドで@availble(iOS 13.0, *)を呼べばいいだけのようです。

AppDelegate.swift
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.

        if #available(iOS 13.0, *) {

        } else {
            //iOS13以前のときだけ呼ばれる
            window = UIWindow(frame: UIScreen.main.bounds)
            window?.rootViewController = ViewController()
            window?.makeKeyAndVisible()
        }

        return true
    }

    // MARK: UISceneSession Lifecycle

    @available(iOS 13.0, *)
    func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
        // Called when a new scene session is being created.
        // Use this method to select a configuration to create the new scene with.
        return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
    }

    @available(iOS 13.0, *)
    func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
        // Called when the user discards a scene session.
        // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
        // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
    }


}
SceneDelegate.swift
@available(iOS 13.0, *)
class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow?

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        guard let scene = (scene as? UIWindowScene) else { return }
        window = UIWindow(windowScene: scene)
        window?.rootViewController = ViewController()
        window?.makeKeyAndVisible()
    }
    func sceneDidDisconnect(_ scene: UIScene) {...}

    func sceneDidBecomeActive(_ scene: UIScene) {...}

    func sceneWillResignActive(_ scene: UIScene) {...}

    func sceneWillEnterForeground(_ scene: UIScene) {...}

    func sceneDidEnterBackground(_ scene: UIScene) {...}

}

参考にした記事

Storyboard 抜きで、コードオンリーで iOS アプリの UI を作る
Xcode11で作成したプロジェクトを古いOSに対応させる(とりあえず版)
Xcode11で作成したプロジェクトを古いOSに対応させる(完結編)
iOS13のSceneDelegate周りのアプリの起動シーケンス

さいごに

とても簡単なことですがCould not find a storyboard named 'Main' in bundle NSBundleというエラーで行き詰まってから気がつくまでだいぶ時間がかかったので同じところで困ってる人に役に立ったら幸いです!
もし間違っているところなどありましたら教えていただけると嬉しいです!

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした