Как исправить предупреждение 'keyWindow' was deprecated in iOS 13.0 в Swift?
Проблема и её корень
Предупреждение 'keyWindow' was deprecated in iOS 13.0 связано с переходом Apple на многоконную архитектуру через UIScene. Начиная с iOS 13, приложение может иметь несколько окон (сцен), и понятие "главного окна" (keyWindow) стало неопределённым. Вместо этого нужно работать с окнами конкретной сцены (UIWindowScene).
Решение: получение окна через UIWindowScene
Самый надёжный способ — получить окно из активной сцены. Вот обновлённая версия вашего метода:
func showLoadingIndicator() {
// 1. Получаем активную сцену (окно)
guard let windowScene = UIApplication.shared.connectedScenes
.compactMap({ $0 as? UIWindowScene })
.first(where: { $0.activationState == .foregroundActive }),
let window = windowScene.windows.first else {
print("Нет активного окна")
return
}
// 2. Создаём и добавляем вью
let loadingView = UIView(frame: window.bounds)
loadingView.backgroundColor = UIColor.black.withAlphaComponent(0.5)
window.addSubview(loadingView)
}
Объяснение по шагам:
UIApplication.shared.connectedScenes— возвращает все подключённые сцены (включая фоновые и активные)..compactMap({ $0 as? UIWindowScene })— фильтрует только сцены типаUIWindowScene(игнорирует, например,CPTemplateApplicationSceneдля CarPlay)..first(where: { $0.activationState == .foregroundActive })— выбирает активную на переднем плане сцену (самую релевантную для пользователя).windows.first— берёт первое окно этой сцены (обычно оно одно).
Альтернативный подход: через SceneDelegate
Если вам нужно часто обращаться к окну, можно сохранить ссылку на него в SceneDelegate:
// В SceneDelegate.swift
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?.makeKeyAndVisible()
}
}
Затем в любом месте приложения:
if let window = (UIApplication.shared.connectedScenes.first as? UIWindowScene)?.delegate as? SceneDelegate {
window.showLoadingIndicator() // или window?.window?.addSubview(...)
}
Важные замечания:
- Не используйте
UIApplication.shared.windows.first— это свойство тоже устарело и может вернуть окно из неактивной сцены. - Если приложение поддерживает мультизадачность (iPad), убедитесь, что вы работаете именно с тем окном, которое видит пользователь.
- Для простых случаев (одно окно) можно использовать замыкание
UIApplication.shared.connectedScenes.first, но это менее надёжно.
После замены кода предупреждение исчезнет, и приложение будет корректно работать на iOS 13+.
<<Быстрое решение для однооконных приложений
Если ваше приложение точно не поддерживает несколько сцен (например, вы не используете мультизадачность на iPad), можно воспользоваться упрощённым подходом через UIWindowScene:
func showLoadingIndicator() {
guard let scene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
let window = scene.windows.first else {
return
}
let loadingView = UIView(frame: window.bounds)
loadingView.backgroundColor = UIColor.black.withAlphaComponent(0.5)
window.addSubview(loadingView)
}
Этот код просто берёт первую подключённую сцену. Он короче, но менее надёжен: если сцена ещё не активирована (например, приложение в фоне), windows.first может вернуть nil.
Когда использовать:
- Приложение только для iPhone (одно окно).
- Быстрый рефакторинг старого кода.
- Прототипирование или учебные проекты.
Недостатки:
- Может вернуть окно из фоновой сцены (например, если пользователь свернул приложение).
- Не подходит для iPad с Split View или Slide Over.
Рекомендую использовать первый ответ для продакшн-кода, а этот — только для быстрых исправлений.
Ответов пока нет.
Войдите чтобы оставить ответ
Блоги