Чтобы получить root-контроллер, нужно глянуть на иерархию приложения.
Scenes (Сцены) для iOS 13 и выше
Наглядно UI-архитектура с iOS 13 на картинке:
На экране может быть несколько сцен, а у сцен несколько окон. Для каждого окна свой root-контроллер, а это значит что у приложения может быть больше одного root-контроллера.
Допустим вы ищите root-контроллер только для активной сцены, отфильтруем их:
// Window есть только у <mark>UIWindowScene</mark>:
let windowScenes = UIApplication.shared.connectedScenes.compactMap { $0 as? UIWindowScene }
// Получаем активные:
let activeScenes = windowScenes.filter { $0.activationState == .foregroundActive }
Теперь у сцены можно получить keyWindow:
let firstActiveScene = activeScene.first
let keyWindow = firstActiveScene?.keyWindow?.rootViewController
Но на экране может быть два равнозначных окна. Например, две заметки в Split-режиме на iPad. В переборе вам нужно выбрать главную сцену и контроллер вручную. Сделать это можно через проверку типа:
// Получаем сцену по классу делегата:
let scene = windowScenes.first(where: { ($0.delegate as? RootSceneDelegate) != nil })
// Перебираем окна с нужным root-контроллером:
let controller = scene?.windows.first(where: { $0.rootViewController as? RootSplitController != nil })
Windows (Окна) для iOS 12 и ниже
До iOS 13 были только Window. Root-контроллер можно получить однозначно - с ним запускается приложение:
Чтобы получить root, нужно получить key-window и обратится к rootViewController:
// Главное окно -> главный контроллер
UIApplication.shared.keyWindow?.rootViewController
Альтернативный способ обратится к массиву окон и взять первое:
UIApplication.shared.windows.first?.rootViewController
Первое окно всегда было root, потому что с ним запускалось приложение.
Для SwiftUI
Вы можете сохранить root-view и передать её как параметр. Но если вы хотите доступ через UIKit, то вызов UIApplication работает и для SwiftUI.
Если вы хотите развернуть root-контроллер красиво, например, получить UISplitViewController из UIKit в коде SwiftUI, попробуйте библиотеку SwiftUI Introspect:
Так например для root-вью NavigationSplitView:
NavigationSplitView {
Text("Root")
} detail: {
Text("Detail")
}
.introspect(.navigationSplitView, on: .iOS(.v16, .v17)) {
print(type(of: $0)) // Здесь UISplitViewController
}