Ottenere riferimento alla visualizzazione / finestra più in alto nell'applicazione iOS


97

Sto creando un framework riutilizzabile per la visualizzazione delle notifiche in un'applicazione iOS. Vorrei che le visualizzazioni di notifica fossero aggiunte sopra tutto il resto nell'applicazione, un po 'come un UIAlertView. Quando avvio il gestore che ascolta gli eventi NSNotification e aggiunge le visualizzazioni in risposta, è necessario ottenere un riferimento alla visualizzazione più in alto nell'applicazione. Questo è quello che ho al momento:

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

Funzionerebbe per qualsiasi applicazione iOS o è un modo più sicuro / migliore per ottenere la vista dall'alto?

Risposte:


36

Di solito questo ti darà la vista dall'alto, ma non c'è alcuna garanzia che sia visibile all'utente. Potrebbe essere fuori dallo schermo, avere un alfa di 0,0 o potrebbe avere una dimensione di 0x0, ad esempio.

Potrebbe anche essere che la keyWindow non abbia viste secondarie, quindi dovresti probabilmente testare prima quella. Sarebbe insolito, ma non impossibile.

UIWindow è una sottoclasse di UIView, quindi se vuoi assicurarti che la tua notifica sia visibile all'utente, puoi aggiungerla direttamente alla keyWindow usando addSubview:e sarà immediatamente la vista più in alto. Non sono sicuro che questo sia ciò che stai cercando di fare però. (Sulla base della tua domanda, sembra che tu lo sappia già.)


111

Ogni volta che voglio visualizzare un overlay sopra tutto il resto, lo aggiungo direttamente nella parte superiore della finestra dell'applicazione:

[[[UIApplication sharedApplication] keyWindow] addSubview:someView]

9
Funziona solo in orientamento verticale .. devi preoccupate per rotazioni ecc come questo: stackoverflow.com/questions/2508630/...
hfossli

1
Ricevo la (null)mia app iOS-8 (problema con gli storyboard?) Qualche suggerimento? Grazie! (Nota: (null)restituito sia alle viewDidLoadche viewWillAppear:all'ora. viewDidAppear:È troppo tardi.
Olie

funziona quando presentiamo un controller di visualizzazione ?. La vista secondaria aggiunta verrà mostrata sul controller della vista presentato?
Pratyusha Terli

Questo non andrà sopra la tastiera, ma lastObject lo fa.
Ser Pounce

Questo framework può funzionare in tutti gli orientamenti. E andrà sopra la tastiera. github.com/HarrisonXi/TopmostView
Harrison Xi

47

Ci sono due parti del problema: finestra superiore, vista dall'alto sulla finestra superiore.

Tutte le risposte esistenti mancavano della parte superiore della finestra. Ma [[UIApplication sharedApplication] keyWindow]non è garantito che sia la finestra in alto.

  1. Finestra superiore. È molto improbabile che ci siano due finestre con la stessa windowLevelcoesistenza per un'app, quindi possiamo ordinare tutte le finestre windowLevele ottenere quella più in alto.

    UIWindow *topWindow = [[[UIApplication sharedApplication].windows sortedArrayUsingComparator:^NSComparisonResult(UIWindow *win1, UIWindow *win2) {
        return win1.windowLevel - win2.windowLevel;
    }] lastObject];
  2. Vista dall'alto sulla finestra superiore. Solo per essere completo. Come già sottolineato nella domanda:

    UIView *topView = [[topWindow subviews] lastObject];

5
Questa soluzione ha smesso di funzionare per me una volta che UIAlertViews è entrato in gioco - sembra che lascino alle spalle una UIWindow vuota, quindi sono tornato atopView = [[[[UIApplication sharedApplication] keyWindow] subviews] lastObject];
Gereon

4
Questo non funziona se la tastiera è alzata. Poiché questa sarà la finestra più in alto
entropia

@Gereon, assicurati di non tenere un forte riferimento a qualsiasi UIAlertView. Se non è un riferimento debole, UIAlertOverlayWindow sarà ancora nella gerarchia e riconosciuto come finestra chiave, ma le visualizzazioni secondarie aggiunte non verranno visualizzate.
beebcon

Il valore della finestra non è corretto nel caso di iOS 8
Mehul Thakkar

11

In realtà potrebbe esserci più di una UIWindow nella tua applicazione. Ad esempio, se una tastiera è sullo schermo [[UIApplication sharedApplication] windows], conterrà almeno due finestre (la finestra dei tasti e la finestra della tastiera).

Quindi, se vuoi che la tua vista appaia sopra entrambi, devi fare qualcosa del tipo:

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

(Supponendo che lastObject contenga la finestra con la priorità windowLevel più alta).


[[[UIApplication sharedApplication] windows] lastObject] valore non corretto in caso di ios 8
Mehul Thakkar

Ho iniziato a usarlo, ma a volte sembra che la finestra della tastiera esista ma non viene visualizzata, e quindi la mia vista non viene visualizzata affatto!
teradyl

3

Mi attengo alla domanda come afferma il titolo e non alla discussione. Quale vista è visibile in alto in un dato punto?

@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

Può essere utilizzato direttamente con qualsiasi UIWindow come ricevitore o qualsiasi UIView come ricevitore.


topWindow fornisce un valore sbagliato in caso di iOS 8, puoi aggiornare la tua risposta per iOS 8
Mehul Thakkar

1
Non ho tempo per scoprirlo. Se lo fai, sei libero di aggiornare questo post.
hfossli

1

Se stai aggiungendo una visualizzazione di caricamento (ad esempio una visualizzazione dell'indicatore di attività), assicurati di avere un oggetto della classe UIWindow. Se mostri un foglio di azione appena prima di mostrare la visualizzazione di caricamento, keyWindowsarà UIActionSheete non UIWindow. E poiché il foglio delle azioni scomparirà, la visualizzazione di caricamento scomparirà con esso. O era questo che mi stava causando problemi.

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;
        }
    }
}

1

Se la tua applicazione funziona solo in orientamento verticale, questo è sufficiente:

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

E la tua vista non verrà mostrata sulla tastiera e sulla barra di stato.

Se vuoi ottenere una vista dall'alto sulla tastiera o sulla barra di stato, o vuoi che la vista più in alto possa ruotare correttamente con i dispositivi, prova questo framework:

https://github.com/HarrisonXi/TopmostView

Supporta iOS7 / 8/9.


0

Usa questo codice se vuoi aggiungere una vista sopra di tutto nello schermo.

[[UIApplication sharedApplication].keyWindow addSubView: yourView];

0

prova questo

UIWindow *window = [[[UIApplication sharedApplication] windows] lastObject];

Informazioni sulla proprietà windows: "Questa proprietà contiene un array di oggetti NSWindow corrispondenti a tutte le finestre attualmente esistenti per l'app. L'array include tutte le finestre sullo schermo e fuori dallo schermo, indipendentemente dal fatto che siano visibili o meno in qualsiasi spazio. Non vi è alcuna garanzia dell'ordine delle finestre nella matrice. "
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;
        }
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.