Uzyskanie odniesienia do najwyższego widoku / okna w aplikacji iOS

97

Tworzę strukturę wielokrotnego użytku do wyświetlania powiadomień w aplikacji na iOS. Chciałbym, aby widoki powiadomień były dodawane ponad wszystko inne w aplikacji, coś w rodzaju UIAlertView. Kiedy uruchamiam menedżera, który nasłuchuje zdarzeń NSNotification i w odpowiedzi dodaje widoki, potrzebuję odwołania do najwyższego widoku w aplikacji. Oto co mam w tej chwili:

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

Czy zadziała to w przypadku dowolnej aplikacji na iOS, czy też jest to bezpieczniejszy / lepszy sposób na uzyskanie widoku z góry?

typoneerror
źródło

Odpowiedzi:

36

Zwykle daje to widok z góry, ale nie ma gwarancji, że jest on widoczny dla użytkownika. Może znajdować się poza ekranem, mieć wartość alfa równą 0,0 lub na przykład mieć rozmiar 0x0.

Może się również zdarzyć, że keyWindow nie ma podglądów podrzędnych, więc prawdopodobnie powinieneś najpierw przetestować. Byłoby to niezwykłe, ale nie jest niemożliwe.

UIWindow jest podklasą UIView, więc jeśli chcesz mieć pewność, że twoje powiadomienie jest widoczne dla użytkownika, możesz dodać je bezpośrednio do keyWindow za pomocą addSubview:i od razu będzie to najwyższy widok. Nie jestem jednak pewien, czy właśnie tego chcesz zrobić. (Na podstawie twojego pytania wygląda na to, że już to wiesz).

Kris Markel
źródło
111

Za każdym razem, gdy chcę wyświetlić jakąś nakładkę na wszystkim innym, po prostu dodaję ją bezpośrednio na górze okna aplikacji:

[[[UIApplication sharedApplication] keyWindow] addSubview:someView]
samvermette
źródło
9
Działa tylko w orientacji pionowej. Musisz przejmować się obrotami itp. W ten sposób: stackoverflow.com/questions/2508630/…
hfossli
1
Dostaję się (null)do mojej aplikacji na iOS-8 (problem ze scenorysami?) Jakieś wskazówki? Dzięki! (Uwaga: (null)zwrócono zarówno o godzinie, jak viewDidLoadio viewWillAppear:godzinie. viewDidAppear:Jest za późno.
Olie
czy to działa, gdy przedstawiamy kontroler widoku ?. Czy dodany widok podrzędny zostanie wyświetlony na kontrolerze prezentowanego widoku?
Pratyusha Terli
To nie wyjdzie ponad klawiaturę, ale lastObject.
Ser Pounce
Ramy te mogą działać we wszystkich orientacjach. I przejdzie nad klawiaturę. github.com/HarrisonXi/TopmostView
Harrison Xi
47

Istnieją dwie części problemu: okno górne, widok z góry w oknie górnym.

Wszystkie istniejące odpowiedzi pominęły górną część okna. Ale [[UIApplication sharedApplication] keyWindow]nie ma gwarancji, że będzie to górne okno.

  1. Górne okno. Jest bardzo mało prawdopodobne, aby windowLeveldla aplikacji istniały dwa takie same okna , więc możemy posortować wszystkie okna windowLeveli uzyskać najwyższe.

    UIWindow *topWindow = [[[UIApplication sharedApplication].windows sortedArrayUsingComparator:^NSComparisonResult(UIWindow *win1, UIWindow *win2) {
        return win1.windowLevel - win2.windowLevel;
    }] lastObject];
  2. Widok z góry w górnym oknie. Żeby być kompletnym. Jak już wskazano w pytaniu:

    UIView *topView = [[topWindow subviews] lastObject];
an0
źródło
5
To rozwiązanie przestało działać dla mnie, gdy pojawił się UIAlertViews - wydaje się, że zostawiają puste okno UIWindow, więc wróciłem dotopView = [[[[UIApplication sharedApplication] keyWindow] subviews] lastObject];
Gereon.
4
To nie działa, jeśli klawiatura jest podniesiona. Ponieważ to będzie najwyższe okno
entropia,
@ Gereon, upewnij się, że nie masz silnego odniesienia do żadnego UIAlertView. Jeśli nie jest to słabe odniesienie, UIAlertOverlayWindow nadal będzie znajdować się w hierarchii i będzie rozpoznawane jako okno klucza, ale dodane do niego widoki podrzędne nie będą wyświetlane.
beebcon
Wartość topWindow nie jest prawidłowa w przypadku iOS 8
Mehul Thakkar
11

W rzeczywistości w aplikacji może znajdować się więcej niż jedno okno UIWindow. Na przykład, jeśli klawiatura jest na ekranie, [[UIApplication sharedApplication] windows]będzie zawierała co najmniej dwa okna (okno z klawiszami i okno klawiatury).

Więc jeśli chcesz, aby twój widok pojawił się na obu z nich, musisz zrobić coś takiego:

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

(Zakładając, że lastObject zawiera okno z najwyższym priorytetem windowLevel).

loreański
źródło
[[[UIApplication sharedApplication] windows] lastObject] wartość nieprawidłowa w przypadku systemu
iOS
Zacząłem z tego korzystać, ale czasami wydaje się, że okno klawiatury istnieje, ale się nie wyświetla, a wtedy mój widok w ogóle się nie wyświetla!
teradyl
3

Trzymam się pytania zgodnie z tytułem, a nie dyskusją. Który widok jest widoczny z góry w danym punkcie?

@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

Może być używany bezpośrednio z dowolnym UIWindow jako odbiornikiem lub dowolnym UIView jako odbiornikiem.

hfossli
źródło
topWindow podaje nieprawidłową wartość w przypadku iOS 8, czy możesz zaktualizować swoją odpowiedź dla iOS 8
Mehul Thakkar,
1
Nie mam czasu się dowiedzieć. Jeśli tak, możesz zaktualizować ten post.
hfossli
1

Jeśli dodajesz widok ładowania (na przykład widok wskaźnika aktywności), upewnij się, że masz obiekt klasy UIWindow. Jeśli pokażesz arkusz akcji tuż przed pokazaniem widoku ładowania, keyWindowbędzie to UIActionSheeti nie UIWindow. A ponieważ arkusz akcji zniknie, widok ładowania zniknie wraz z nim. Albo właśnie to sprawiało mi problemy.

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
źródło
1

Jeśli Twoja aplikacja działa tylko w orientacji pionowej, to wystarczy:

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

Twój widok nie będzie widoczny na klawiaturze i pasku stanu.

Jeśli chcesz uzyskać najwyższy widok, który znajduje się na klawiaturze lub pasku stanu, lub chcesz, aby najwyższy widok mógł się poprawnie obracać z urządzeniami, wypróbuj tę strukturę:

https://github.com/HarrisonXi/TopmostView

Obsługuje iOS7 / 8/9.

Harrison Xi
źródło
0

Po prostu użyj tego kodu, jeśli chcesz dodać widok powyżej wszystkiego na ekranie.

[[UIApplication sharedApplication].keyWindow addSubView: yourView];
handiansom
źródło
0

Spróbuj tego

UIWindow *window = [[[UIApplication sharedApplication] windows] lastObject];
Chandramani
źródło
O właściwości Windows: „Ta właściwość zawiera tablicę obiektów NSWindow odpowiadającą wszystkim aktualnie istniejącym oknom aplikacji. Tablica obejmuje wszystkie okna ekranowe i pozaekranowe, niezależnie od tego, czy są one widoczne w jakimkolwiek miejscu. Nie ma gwarancji kolejności okien w tablicy. "
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;
        }
    }
}
user12775146
źródło