Xcode11で作成したプロジェクトでは`[UIWindow new]`で生成したWindowが画面に表示されない問題の対処法


Xcode11とUISceneとUIWindowにまつわるトラブルシュートに追われている。
個別のケースに分解してお届け。

現象

  • Xcode11で作成したプロジェクトをiOS13で実行しているとき、UIWindowが表示されない
  • rootViewController持たせて、viewDidLoadにbreak pointを貼ったりすると、動作はしていることが確認できる。

原因

  • Xcode11で作る新規プロジェクトは、Scene Based Lifecycleが有効になっている。
  • Scene Based Lifecycleが有効になっていると、[UIWindow new], [[UIWindow alloc] initWithFrame:] で生成したwindowは View Hierarchyに載らなくなる
    • UIWindowを載せるべきWindowSceneが不明なため

対応

ひとつは、Xcode11で作成したプロジェクトからSceneに関する機能をオミットする方法。

もうひとつは正攻法。(本記事)

[[UIWindow alloc] initWithWindowScene:]で windowを生成する。

そのためには、なんらかの手段によってUIWindowSceneを拾い上げる必要がある。

ボタンを押した時に表示したいとか、特定のViewと同じUIWindowSceneを選択する場合

ViewController.m
@interface ViewController ()
@property (nonatomic) UIWindow *window;
@end

@implementation ViewController
- (IBAction)openWindow:(UIButton *)sender {

    UIWindowScene *scene = sender.window.windowScene;
    UIWindow *window = [[UIWindow alloc] initWithWindowScene: scene];

    // 以下は確認用の見た目調整と表示
    UIViewController *viewController = [UIViewController new];
    viewController.view.backgroundColor = UIColer.greenColer;
    window.frame = CGRectMake(20, 20, 50, 50);
    window.rootViewContoller = viewController;

    self.window = window;
    [self.window makeKeyAndVisible];
}
@end
ViewController.swift
class ViewController: UIViewController {
    var window: UIWindow?

    @IBAction func openWindow(_ sender: UIButton) {

        guard let scene = sender.window?.windowScene { return }
        let window = UIWindow(windowScene: scene)

        // 以下は確認用の見た目調整と表示
        let viewController = UIViewController()
        viewController.view.backgroundColer = .green
        window.frame = CGRect(x:20, y:20, width:50, height:50)
        window.rootViewContoller = viewController

        self.window = window
        self.window?.makeKeyAndVisible();
    }
}

Sceneは一つだけのアプリである場合など、UIApplicationからUIWindowSceneを引っ張りたい場合

ViewController.m
@interface ViewController ()
@property (nonatomic) UIWindow *window;
@end

@implementation ViewController
- (IBAction)openWindow:(UIButton *)sender {

    UIWindowScene *scene = (UIWindowScene*)[[[[UIApplication sharedApplication] connectedScenes] allObjects] first];
    UIWindow *window = [[UIWindow alloc] initWithWindowScene: scene];

    // 以下は確認用の見た目調整と表示
    UIViewController *viewController = [UIViewController new];
    viewController.view.backgroundColor = UIColer.greenColer;
    window.frame = CGRectMake(20, 20, 50, 50);
    window.rootViewContoller = viewController;

    self.window = window;
    [self.window makeKeyAndVisible];
}
@end
ViewController.swift
class ViewController: UIViewController {
    var window: UIWindow?

    @IBAction func openWindow(_ sender: UIButton) {

        guard let scene = UIApplication.shared.connectedScenes.first as? UIWindowScene { return }
        let window = UIWindow(windowScene: scene)

        // 以下は確認用の見た目調整と表示
        let viewController = UIViewController()
        viewController.view.backgroundColer = .green
        window.frame = CGRect(x:20, y:20, width:50, height:50)
        window.rootViewContoller = viewController

        self.window = window
        self.window?.makeKeyAndVisible();
    }
}

参考URL