Obtendo referência para a visualização / janela superior no aplicativo iOS

97

Estou criando uma estrutura reutilizável para exibir notificações em um aplicativo iOS. Gostaria que as visualizações de notificação fossem adicionadas acima de tudo o mais no aplicativo, como um UIAlertView. Quando eu inicio o gerenciador que escuta os eventos NSNotification e adiciona visualizações em resposta, preciso obter uma referência para a visualização superior do aplicativo. Isso é o que eu tenho no momento:

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

Isso funcionaria para qualquer aplicativo iOS ou é uma maneira mais segura / melhor de obter a vista superior?

typeoneerror
fonte

Respostas:

36

Normalmente, isso dará a você a vista superior, mas não há garantia de que ela seja visível para o usuário. Ele pode estar fora da tela, ter um alfa de 0,0 ou pode ter o tamanho de 0x0, por exemplo.

Também pode ser que a keyWindow não tenha subvisualizações, então você provavelmente deve testar isso primeiro. Isso seria incomum, mas não é impossível.

UIWindow é uma subclasse de UIView, então se você quiser ter certeza de que sua notificação está visível para o usuário, você pode adicioná-la diretamente à keyWindow usando addSubview:e ela será instantaneamente a vista superior. Não tenho certeza se é isso que você está procurando fazer. (Com base na sua pergunta, parece que você já sabe disso.)

Kris Markel
fonte
111

Sempre que desejo exibir alguma sobreposição acima de tudo, simplesmente adiciono diretamente na parte superior da janela do aplicativo:

[[[UIApplication sharedApplication] keyWindow] addSubview:someView]
samvermette
fonte
9
Funciona apenas na orientação retrato. Você deve se preocupar com as rotações etc. como este: stackoverflow.com/questions/2508630/…
hfossli
1
Estou acessando (null)meu aplicativo iOS-8 (problema com storyboards?) Alguma dica? Obrigado! (Observação: (null)retornou às viewDidLoade na viewWillAppear:hora. viewDidAppear:É tarde demais.
Olie
isso funciona quando apresentamos um controlador de visualização ?. A subvisualização adicionada será mostrada sobre o controlador de visão apresentado?
Pratyusha Terli
Isso não irá acima do teclado, embora lastObject vá.
Ser Pounce
Esta estrutura pode funcionar em todas as orientações. E vai acima do teclado. github.com/HarrisonXi/TopmostView
Harrison Xi
47

Existem duas partes do problema: janela superior, vista superior na janela superior.

Todas as respostas existentes perderam a parte superior da janela. Mas [[UIApplication sharedApplication] keyWindow]não é garantido que seja a janela superior.

  1. Janela de cima. É muito improvável que haja duas janelas com a mesma windowLevelcoexistência para um aplicativo, portanto, podemos classificar todas as janelas windowLevele obter a que está mais acima.

    UIWindow *topWindow = [[[UIApplication sharedApplication].windows sortedArrayUsingComparator:^NSComparisonResult(UIWindow *win1, UIWindow *win2) {
        return win1.windowLevel - win2.windowLevel;
    }] lastObject];
    
  2. Vista superior na janela superior. Apenas para ser completo. Como já apontado na pergunta:

    UIView *topView = [[topWindow subviews] lastObject];
an0
fonte
5
Essa solução parou de funcionar para mim quando UIAlertViews entrou em ação - eles parecem deixar uma UIWindow vazia para trás, então voltei paratopView = [[[[UIApplication sharedApplication] keyWindow] subviews] lastObject];
Gereon
4
Isso não funciona se o teclado estiver ativado. Como essa será a janela superior
entropia de
@Gereon, certifique-se de não ter uma referência forte a nenhum UIAlertView. Se não for uma referência fraca, a UIAlertOverlayWindow ainda estará na hierarquia e será reconhecida como a janela principal, mas as subvisualizações adicionadas a ela não serão exibidas.
beebcon
O valor topWindow não é adequado no caso do iOS 8
Mehul Thakkar
11

Na verdade, pode haver mais de uma UIWindow em seu aplicativo. Por exemplo, se houver um teclado na tela, [[UIApplication sharedApplication] windows]ele conterá pelo menos duas janelas (a janela de teclas e a janela do teclado).

Então, se você quiser que sua visualização apareça no topo de ambos, você precisa fazer algo como:

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

(Supondo que lastObject contenha a janela com a maior prioridade windowLevel).

loreano
fonte
O valor [[[UIApplication sharedApplication] windows] lastObject] não é adequado no caso de ios 8
Mehul Thakkar
Comecei a usar isso, mas às vezes parece que a janela do teclado existe, mas não está sendo exibida, e minha visualização não é exibida!
teradil de
3

Estou me limitando à pergunta conforme o título indica e não à discussão. Qual vista é visível em qualquer ponto?

@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

Pode ser usado diretamente com qualquer UIWindow como receptor ou qualquer UIView como receptor.

Hfossli
fonte
topWindow está fornecendo um valor errado no caso do iOS 8, você pode atualizar sua resposta para o iOS 8
Mehul Thakkar
1
Não tenho tempo para descobrir. Se você está livre para atualizar este post.
hfossli 01 de
1

Se você estiver adicionando uma visualização de carregamento (uma visualização de indicador de atividade, por exemplo), certifique-se de ter um objeto da classe UIWindow. Se você mostrar uma folha de ação antes de mostrar sua visualização de carregamento, keyWindowserá o UIActionSheete não UIWindow. E como a folha de ação desaparecerá, a visualização de carregamento também desaparecerá. Ou era isso que estava me 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
fonte
1

Se o seu aplicativo funciona apenas na orientação retrato, isso é o suficiente:

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

E sua visualização não será mostrada no teclado e na barra de status.

Se você deseja obter uma visão superior do teclado ou da barra de status, ou se deseja que a visão superior possa girar corretamente com os dispositivos, experimente esta estrutura:

https://github.com/HarrisonXi/TopmostView

Suporta iOS7 / 8/9.

Harrison Xi
fonte
0

Basta usar este código se quiser adicionar uma visão acima de tudo na tela.

[[UIApplication sharedApplication].keyWindow addSubView: yourView];
Handiansom
fonte
0

tente isso

UIWindow *window = [[[UIApplication sharedApplication] windows] lastObject];
Chandramani
fonte
Sobre a propriedade windows: "Esta propriedade contém uma matriz de objetos NSWindow correspondentes a todas as janelas existentes atualmente para o aplicativo. A matriz inclui todas as janelas na tela e fora da tela, estejam ou não visíveis em qualquer espaço. Não há garantia do pedido das janelas na 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;
        }
    }
}
usuário12775146
fonte