iOS Multiple Window Support Without Storyboards
Supporting multiple windows and the UIScene lifecycle API programtically
If, like me, you prefer doing things programatically, instead of using interface builder, then the Main.Storyboard
file doesn't last beyond creating the new project.
However, now with iOS 13 and multiple window there is now a SceneDelegate
as well as an AppDelegate
to deal with.
Fortunately, it isn't much more complex than before, just moved around slightly.
AppDelegate (iOS 12)
With iOS 12 you would setup an application programatically by deleting the Main.Storyboard
and then creating the window in the AppDelegate
's application:willFinishLaunchingWithOptions:
or application:didFinishLaunchingWithOptions:
.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window.rootViewController = ViewController()
self.window.makeKeyAndVisible()
return true
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.rootViewController = [[ViewController alloc] init];
[self.window makeKeyAndVisible];
return YES;
}
SceneDelegate (iOS 13)
The info.plist
file still has the UIMainStoryboardFile
key-value pair, but now it also has a UISceneStoryboardFile
key inside the UIApplicationSceneManifest -> UISceneConfigurations
array.
Once those 2 are deleted, the Storyboard is no longer involved in creating your apps UI and you need to do that in code yourself.
The location to do so is now in the SceneDelegate
's -scene:willConnectToSession:options:
method.
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`.
guard let windowScene = (scene as? UIWindowScene) else { return }
let window = UIWindow(windowScene: windowScene)
window.rootViewController = ViewController()
self.window = window
window.makeKeyAndVisible()
}
- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions {
self.window = [[UIWindow alloc] initWithWindowScene:(UIWindowScene*)scene];
self.window.rootViewController = [[ViewController alloc] init];
[self.window makeKeyAndVisible];
}
The only difference compared to the old -application:willFinishLaunchingWithOptions:
code is that we now need to tell the UIWindow
which scene it is part of. This can be done using the new UIWindow(windowScene: windowScene)
init method, but windows can also be dynamically moved between scenes so there is also a windowScene
property on UIWindow
instances which can be changed at any time.