Obteniendo referencia a la vista / ventana más alta en la aplicación iOS

97

Estoy creando un marco reutilizable para mostrar notificaciones en una aplicación de iOS. Me gustaría que las vistas de notificación se agreguen encima de todo lo demás en la aplicación, algo así como un UIAlertView. Cuando inicio el administrador que escucha los eventos de NSNotification y agrega vistas en respuesta, necesito obtener una referencia a la vista superior en la aplicación. Esto es lo que tengo en este momento:

_topView = [[[[UIApplication sharedApplication] keyWindow] subviews] lastObject];

¿Funcionaría esto para cualquier aplicación de iOS o es una forma más segura / mejor de obtener la vista superior?

typeoneerror
fuente

Respuestas:

36

Por lo general, eso le dará la vista superior, pero no hay garantía de que sea visible para el usuario. Podría estar fuera de la pantalla, tener un alfa de 0.0 o podría tener un tamaño de 0x0, por ejemplo.

También podría ser que keyWindow no tenga subvistas, por lo que probablemente debería probar eso primero. Esto sería inusual, pero no imposible.

UIWindow es una subclase de UIView, por lo que si desea asegurarse de que su notificación sea visible para el usuario, puede agregarla directamente a keyWindow usando addSubview:e instantáneamente será la vista más alta. Sin embargo, no estoy seguro de si esto es lo que estás buscando hacer. (Según su pregunta, parece que ya lo sabe).

Kris Markel
fuente
111

Siempre que quiero mostrar una superposición encima de todo lo demás, simplemente la agrego en la parte superior de la ventana de la aplicación directamente:

[[[UIApplication sharedApplication] keyWindow] addSubview:someView]
samvermette
fuente
9
Solo funciona en orientación vertical. Debes preocuparte por las rotaciones, etc. de esta manera: stackoverflow.com/questions/2508630/…
hfossli
1
Estoy ingresando (null)a mi aplicación iOS-8 (¿problema con los guiones gráficos?) ¿Alguna sugerencia? ¡Gracias! (Nota: (null)devuelto tanto a la hora viewDidLoadcomo a la viewWillAppear:hora. viewDidAppear:Es demasiado tarde.
Olie
¿Funciona esto cuando presentamos un controlador de vista ?. ¿Se mostrará la subvista agregada sobre el controlador de vista presentado?
Pratyusha Terli
Esto no irá por encima del teclado, aunque lastObject sí.
Ser Pounce
Este marco puede funcionar en todas las orientaciones. E irá por encima del teclado. github.com/HarrisonXi/TopmostView
Harrison Xi
47

Hay dos partes del problema: ventana superior, vista superior en la ventana superior.

Todas las respuestas existentes fallaron en la parte superior de la ventana. Pero [[UIApplication sharedApplication] keyWindow]no se garantiza que sea la ventana superior.

  1. Ventana superior. Es muy poco probable que windowLevelcoexistan dos ventanas con la misma para una aplicación, por lo que podemos ordenar todas las ventanas windowLevely obtener la más alta.

    UIWindow *topWindow = [[[UIApplication sharedApplication].windows sortedArrayUsingComparator:^NSComparisonResult(UIWindow *win1, UIWindow *win2) {
        return win1.windowLevel - win2.windowLevel;
    }] lastObject];
  2. Vista superior de la ventana superior. Solo para estar completo. Como ya se señaló en la pregunta:

    UIView *topView = [[topWindow subviews] lastObject];
an0
fuente
5
Esta solución dejó de funcionar para mí una vez que UIAlertViews entró en juego; parecen dejar una UIWindow vacía, así que volví atopView = [[[[UIApplication sharedApplication] keyWindow] subviews] lastObject];
Gereon
4
Esto no funciona si el teclado está arriba. Como esa será la ventana más alta
entropía
@Gereon, asegúrese de no tener una referencia sólida a ningún UIAlertView. Si no es una referencia débil, UIAlertOverlayWindow seguirá estando en la jerarquía y se reconocerá como la ventana clave, pero las subvistas agregadas no se mostrarán.
beebcon
El valor de topWindow no es adecuado en el caso de iOS 8
Mehul Thakkar
11

En realidad, podría haber más de una UIWindow en su aplicación. Por ejemplo, si un teclado está en pantalla [[UIApplication sharedApplication] windows], contendrá al menos dos ventanas (su ventana de teclas y la ventana de teclado).

Entonces, si desea que su vista aparezca encima de ambos, debe hacer algo como:

[[[[UIApplication sharedApplication] windows] lastObject] addSubview:view];

(Suponiendo que lastObject contiene la ventana con la mayor prioridad windowLevel).

Lorean
fuente
[[[UIApplication sharedApplication] windows] lastObject] valor no adecuado en el caso de ios 8
Mehul Thakkar
Comencé a usar esto, pero parece que a veces la ventana del teclado existe pero no se muestra, ¡y luego mi vista no se muestra en absoluto!
teradilo
3

Me atengo a la pregunta como dice el título y no a la discusión. ¿Qué vista es visible desde arriba en cualquier punto dado?

@implementation UIView (Extra)

- (UIView *)findTopMostViewForPoint:(CGPoint)point
{
    for(int i = self.subviews.count - 1; i >= 0; i--)
    {
        UIView *subview = [self.subviews objectAtIndex:i];
        if(!subview.hidden && CGRectContainsPoint(subview.frame, point))
        {
            CGPoint pointConverted = [self convertPoint:point toView:subview];
            return [subview findTopMostViewForPoint:pointConverted];
        }
    }

    return self;
}

- (UIWindow *)topmostWindow
{
    UIWindow *topWindow = [[[UIApplication sharedApplication].windows sortedArrayUsingComparator:^NSComparisonResult(UIWindow *win1, UIWindow *win2) {
        return win1.windowLevel - win2.windowLevel;
    }] lastObject];
    return topWindow;
}

@end

Puede usarse directamente con cualquier UIWindow como receptor o cualquier UIView como receptor.

hfossli
fuente
topWindow da un valor incorrecto en el caso de iOS 8, ¿puede actualizar su respuesta para iOS 8
Mehul Thakkar
1
No tengo tiempo para averiguarlo. Si lo hace, puede actualizar esta publicación.
hfossli
1

Si está agregando una vista de carga (una vista de indicador de actividad, por ejemplo), asegúrese de tener un objeto de la clase UIWindow. Si muestra una hoja de acción justo antes de mostrar su vista de carga, keyWindowserá el UIActionSheety no UIWindow. Y dado que la hoja de acción desaparecerá, la vista de carga desaparecerá con ella. O eso es lo que me estaba causando problemas.

UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow];
if (![NSStringFromClass([keyWindow class]) isEqualToString:@"UIWindow"]) {
    // find uiwindow in windows
    NSArray *windows = [UIApplication sharedApplication].windows;
    for (UIWindow *window in windows) {
        if ([NSStringFromClass([window class]) isEqualToString:@"UIWindow"]) {
            keyWindow = window;
            break;
        }
    }
}
Miha Hribar
fuente
1

Si su aplicación solo funciona en orientación vertical, esto es suficiente:

[[[UIApplication sharedApplication] keyWindow] addSubview:yourView]

Y su vista no se mostrará en el teclado ni en la barra de estado.

Si desea obtener una vista superior sobre el teclado o la barra de estado, o si desea que la vista superior pueda rotar correctamente con los dispositivos, pruebe este marco:

https://github.com/HarrisonXi/TopmostView

Es compatible con iOS7 / 8/9.

Harrison Xi
fuente
0

Solo use este código si desea agregar una vista arriba de todo en la pantalla.

[[UIApplication sharedApplication].keyWindow addSubView: yourView];
handiansom
fuente
0

prueba esto

UIWindow *window = [[[UIApplication sharedApplication] windows] lastObject];
Chandramani
fuente
Acerca de la propiedad de windows: "Esta propiedad contiene una matriz de objetos NSWindow que corresponden a todas las ventanas existentes para la aplicación. La matriz incluye todas las ventanas en pantalla y fuera de la pantalla, estén o no visibles en cualquier espacio. No hay garantía del pedido de las ventanas de la matriz ".
Steven Fisher
0
UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow];
if (![NSStringFromClass([keyWindow class]) isEqualToString:@"UIWindow"]) {

    NSArray *windows = [UIApplication sharedApplication].windows;
    for (UIWindow *window in windows) {
        if ([NSStringFromClass([window class]) isEqualToString:@"UIWindow"]) {
            keyWindow = window;
            break;
        }
    }
}
usuario12775146
fuente