SceneDelegateやWindowなどについて改めて調べてみました
SceneDelegateはiPadのマルチウィンドウで使われます
具体的にはSplit ViewやSlide overなどの時に活躍します
検証のためにマルチウインドウ対応のアプリを作ってみます
変更自体は簡単でInfo.plistでEnable Multiple Windows
をYESに変える事で変更ができます
アプリを起動すると下のようにSplit Viewでアプリを表示できました
ログを見たところ、SceneDelegateのwillConnectToメソッドはアプリ起動時とSplit Viewで表示する時の2回呼ばれている事を確認できました
class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { print(#function) guard let _ = (scene as? UIWindowScene) else { return } } }
現在表示されているUIScene一覧の取得方法は下の通りです
UIApplication.shared.connectedScenes
SwiftUIではSceneDelegateは使わない事が増えています
代わりに下のようにscenePhaseを使います
AppDelegateの代わりはAppのinitなどが使われています
@main struct MyAppApp: App { @Environment(\.scenePhase) private var scenePhase var body: some Scene { WindowGroup { ContentView().onChange(of: scenePhase) { oldPhase, newPhase in switch newPhase { case .active: print("アクティブ") case .inactive: print("非アクティブ") case .background: print("バックグラウンド") @unknown default: break } } } } init() { print("App init") } }
引き続きUIKitについて見ていくと、UIWindowはSceneDelegateごとに作られるものになります
UIWindowは明示的に作成しなくても自動的にセットされます
class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { print(window) // → <UIWindow: 0x105313870; frame = (0 0; 1024 1366); hidden = YES; gestureRecognizers = <NSArray: 0x60000174f340>; layer = <UIWindowLayer: 0x600000c84630>> guard let _ = (scene as? UIWindowScene) else { return } } func sceneDidDisconnect(_ scene: UIScene) { } func sceneDidBecomeActive(_ scene: UIScene) { } func sceneWillResignActive(_ scene: UIScene) { } func sceneWillEnterForeground(_ scene: UIScene) { } func sceneDidEnterBackground(_ scene: UIScene) { } }
下のようにする事で自分でUIWindowの初期化ができます
class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { guard let windowScene = (scene as? UIWindowScene) else { return } window = UIWindow(windowScene: windowScene) window?.rootViewController = TestViewController() window?.makeKeyAndVisible() } func sceneDidDisconnect(_ scene: UIScene) { } func sceneDidBecomeActive(_ scene: UIScene) { } func sceneWillResignActive(_ scene: UIScene) { } func sceneWillEnterForeground(_ scene: UIScene) { } func sceneDidEnterBackground(_ scene: UIScene) { } } class TestViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .white let label = UILabel() label.text = "TEST2" label.translatesAutoresizingMaskIntoConstraints = false view.addSubview(label) NSLayoutConstraint.activate([ label.centerXAnchor.constraint(equalTo: view.centerXAnchor), label.centerYAnchor.constraint(equalTo: view.centerYAnchor) ]) } }
UISceneではなく画面全体のサイズを取りたい時についてはUIScreenを利用します
UIScreen.main.bounds