Cómo resolver: 'keyWindow' quedó obsoleto en iOS 13.0

Resuelto Hardy asked hace 5 años • 30 respuestas

Estoy usando Core Data con Cloud Kit y, por lo tanto, debo verificar el estado del usuario de iCloud durante el inicio de la aplicación. En caso de problemas, quiero enviar un cuadro de diálogo al usuario y lo hago usando UIApplication.shared.keyWindow?.rootViewController?.present(...)hasta ahora.

En Xcode 11 beta 4, ahora hay un nuevo mensaje de obsolescencia que me dice:

'keyWindow' quedó obsoleto en iOS 13.0: no debe usarse para aplicaciones que admitan múltiples escenas, ya que devuelve una ventana clave en todas las escenas conectadas.

captura de pantalla

¿Cómo debo presentar el diálogo en su lugar?

Hardy avatar Jul 21 '19 21:07 Hardy
Aceptado

Editar La sugerencia que hago aquí está obsoleta en iOS 15. ¿Y ahora qué? Bueno, si una aplicación no tiene varias ventanas propias, supongo que la forma moderna aceptada sería obtener la primera de la aplicación connectedScenes, forzarla a UIWindowScene y tomar su primera ventana. ¡Pero eso es casi exactamente lo que hace la respuesta aceptada! Así que mi solución parece bastante débil en este momento. Sin embargo, lo dejaré así por razones históricas.


La respuesta aceptada, aunque ingeniosa, podría ser demasiado elaborada. Puedes obtener exactamente el mismo resultado de forma mucho más sencilla:

UIApplication.shared.windows.filter {$0.isKeyWindow}.first

También advertiría que la desaprobación de keyWindowno debe tomarse demasiado en serio. El mensaje de advertencia completo dice:

'keyWindow' quedó obsoleto en iOS 13.0: no debe usarse para aplicaciones que admitan múltiples escenas, ya que devuelve una ventana clave en todas las escenas conectadas.

Entonces, si no admite múltiples ventanas en iPad, no hay objeción en seguir adelante y continuar usándolo keyWindow.

matt avatar Sep 12 '2019 02:09 matt

iOS 16-17, compatible hasta iOS 15

Como este hilo sigue recibiendo tráfico tres años después, quiero compartir lo que considero la solución más elegante con la funcionalidad actual. También funciona con SwiftUI.

UIApplication
    .shared
    .connectedScenes
    .compactMap { ($0 as? UIWindowScene)?.keyWindow }
    .last

iOS 15 y 16, compatible hasta iOS 13

UIApplication
    .shared
    .connectedScenes
    .flatMap { ($0 as? UIWindowScene)?.windows ?? [] }
    .last { $0.isKeyWindow }

Tenga en cuenta que connectedScenessolo está disponible desde iOS 13. Si necesita admitir versiones anteriores de iOS, debe incluirlo en una if #available(iOS 13, *)declaración.

Una variante más larga, pero más fácil de entender:

UIApplication
    .shared
    .connectedScenes
    .compactMap { $0 as? UIWindowScene }
    .flatMap { $0.windows }
    .last { $0.isKeyWindow }

iOS 13 y 14

La siguiente respuesta histórica sigue siendo válida en iOS 15, pero debe reemplazarse porque UIApplication.shared.windowsestá obsoleta. ¡Gracias a @matt por señalar esto!

Respuesta original:

Mejorando ligeramente la excelente respuesta de Matt, esto es aún más simple, más corto y más elegante:

UIApplication.shared.windows.last { $0.isKeyWindow }

Actualización en abril de 2023:

Las versiones anteriores de esta respuesta seleccionaron en firstlugar lastde varias ventanas clave. Como señalaron @TengL y @Rob en los comentarios, esto podría generar un comportamiento inconsistente. Peor aún, la solución iOS 13/14 seleccionaría una ventana que podría estar oculta detrás de otra. Las soluciones iOS 16/15 también pueden provocar este problema, aunque no existe una especificación exacta.

Por lo tanto, he actualizado las cuatro variantes de la solución para aumentar las posibilidades de que la ventana clave seleccionada sea realmente visible. Esto debería ser suficiente para la mayoría de las aplicaciones que se ejecutan en iOS. Se puede obtener un control más preciso de las aplicaciones en iPadOS, especialmente cuando se ejecutan en macOS, ordenando escenas por su función activationStateo su función personalizada.

pommy avatar Sep 20 '2019 16:09 pommy

Esta es mi solución:

let keyWindow = UIApplication.shared.connectedScenes
        .filter({$0.activationState == .foregroundActive})
        .compactMap({$0 as? UIWindowScene})
        .first?.windows
        .filter({$0.isKeyWindow}).first

Uso por ejemplo:

keyWindow?.endEditing(true)
berni avatar Jul 23 '2019 17:07 berni