Часто нужно перекрыть основной интерфейс:
- Показать алерт с ошибкой поверх всего.
- Показать индикатор загрузки поверх всего.
- Показать экран загрузки после старта приложения.
- Показать экран входа если пользователь не авторизован.
До недавнего времени я считал основными два способа:
- Переопределить рутконтроллер основного окна.
- Добавить сабвью к основному окну.
Первый способ это прям «лучшая практика» у индусов с медиума, второй тоже практикуется, и оба выглядят как костыли.
Оказалось (стояло взглянуть на проблему шире) есть простой и эффективный способ решения проблемы. Нужно создать ещё одно окно поверх основного, основное при этом не трогая.
Для этого нужно:
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var loginWindow: UIWindow? // 1
var loggedIn = false
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
if !loggedIn {
let loginViewController = UIViewController()
loginViewController.view.backgroundColor = .red
loginWindow = UIWindow(frame: UIScreen.main.bounds) // 2
loginWindow!.rootViewController = loginViewController
loginWindow!.windowLevel = .normal + 1 // 3
loginWindow!.makeKeyAndVisible() // 4
}
return true
}
}
- Создать ещё одну переменную с UIWindow.
- Создать окно.
- Присвоить windowLevel, это z-index окна, normal + 1 будет выше главного окна и будет виден статус бар.
- Показывает и делает ключевым это окно (isHidden = false, isKeyWindow = true).
Если запустить дебаггер можно увидеть два окна и иерархии:
Алерт показать/скрыть можно так:
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var alertWindow: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
DispatchQueue.main.asyncAfter(deadline: .now() + 1) { [weak self] in
self!.alertWindow = UIWindow(frame: UIScreen.main.bounds)
self!.alertWindow!.rootViewController = UIViewController()
self!.alertWindow!.windowLevel = .alert + 1 // 1
self!.alertWindow!.makeKeyAndVisible()
let alert = UIAlertController(title: "Hello1", message: nil, preferredStyle: .alert)
self!.alertWindow!.rootViewController!.present(alert, animated: true)
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel) { _ in
self!.alertWindow?.isHidden = true // 2
self!.alertWindow = nil // 3
})
}
return true
}
}
- Alert + 1 выше всех, даже статус бар перекрывает.
- Скрывает окно и передаёт статус ключевого, ближайшему окну в иерархии.
- При обнулении ссылки происходит удаление окна.
Иерархия выглядет так: