Verweis auf die oberste Ansicht / das oberste Fenster in der iOS-Anwendung

97

Ich erstelle ein wiederverwendbares Framework für die Anzeige von Benachrichtigungen in einer iOS-Anwendung. Ich möchte, dass die Benachrichtigungsansichten über alles andere in der Anwendung hinzugefügt werden, ähnlich wie bei einer UIAlertView. Wenn ich den Manager initiiere, der auf NSNotification-Ereignisse wartet und als Antwort Ansichten hinzufügt, muss ich einen Verweis auf die oberste Ansicht in der Anwendung erhalten. Das habe ich im Moment:

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

Würde dies für jede iOS-Anwendung funktionieren oder ist dies eine sicherere / bessere Möglichkeit, die Draufsicht zu erhalten?

typoneerror
quelle

Antworten:

36

Normalerweise erhalten Sie dadurch die Draufsicht, es gibt jedoch keine Garantie dafür, dass sie für den Benutzer sichtbar ist. Es könnte außerhalb des Bildschirms sein, ein Alpha von 0,0 haben oder beispielsweise eine Größe von 0x0 haben.

Es kann auch sein, dass das keyWindow keine Unteransichten hat, daher sollten Sie dies wahrscheinlich zuerst testen. Das wäre ungewöhnlich, aber nicht unmöglich.

UIWindow ist eine Unterklasse von UIView. Wenn Sie also sicherstellen möchten, dass Ihre Benachrichtigung für den Benutzer sichtbar ist, können Sie sie direkt mit keyWindow hinzufügen addSubview:und sie ist sofort die oberste Ansicht. Ich bin mir nicht sicher, ob Sie dies tun möchten. (Aufgrund Ihrer Frage scheinen Sie dies bereits zu wissen.)

Kris Markel
quelle
111

Wann immer ich etwas Overlay über alles andere anzeigen möchte, füge ich es einfach direkt über dem Anwendungsfenster hinzu:

[[[UIApplication sharedApplication] keyWindow] addSubview:someView]
Samvermette
quelle
9
Funktioniert nur im Hochformat. Sie müssen sich um Rotationen usw. wie diese kümmern
hfossli
1
Ich erhalte (null)meine iOS-8-App (Storyboard-Problem?). Irgendwelche Hinweise? Vielen Dank! (Hinweis: (null)zurück beides viewDidLoadund viewWillAppear:Zeit. viewDidAppear:Ist zu spät.
Olie
Funktioniert dies, wenn wir einen View Controller präsentieren? Wird die hinzugefügte Unteransicht über dem dargestellten Ansichtscontroller angezeigt?
Pratyusha Terli
Dies wird jedoch nicht über die Tastatur hinausgehen, lastObject jedoch.
Ser Pounce
Dieser Rahmen kann in allen Ausrichtungen funktionieren. Und wird über die Tastatur gehen. github.com/HarrisonXi/TopmostView
Harrison Xi
47

Es gibt zwei Teile des Problems: Oberes Fenster, Draufsicht auf das obere Fenster.

Alle vorhandenen Antworten haben den oberen Fensterteil verfehlt. Es [[UIApplication sharedApplication] keyWindow]ist jedoch nicht garantiert, dass es das oberste Fenster ist.

  1. Oberes Fenster. Es ist sehr unwahrscheinlich, dass windowLevelfür eine App zwei Fenster mit derselben Koexistenz vorhanden sind, sodass wir alle Fenster nach sortieren windowLevelund das oberste erhalten können.

    UIWindow *topWindow = [[[UIApplication sharedApplication].windows sortedArrayUsingComparator:^NSComparisonResult(UIWindow *win1, UIWindow *win2) {
        return win1.windowLevel - win2.windowLevel;
    }] lastObject];
  2. Draufsicht auf das obere Fenster. Nur um vollständig zu sein. Wie bereits in der Frage ausgeführt:

    UIView *topView = [[topWindow subviews] lastObject];
an0
quelle
5
Diese Lösung funktionierte für mich nicht mehr, als UIAlertViews ins Spiel kamen - sie scheinen ein leeres UIWindow zurückzulassen, also kehrte ich zurück zu topView = [[[[UIApplication sharedApplication] keyWindow] subviews] lastObject];
Gereon am
4
Dies funktioniert nicht, wenn die Tastatur aktiv ist. Da dies das oberste Fenster sein wird
Entropie
@Gereon, stellen Sie sicher, dass Sie keinen starken Verweis auf UIAlertView haben. Wenn es sich nicht um eine schwache Referenz handelt, befindet sich das UIAlertOverlayWindow weiterhin in der Hierarchie und wird als Schlüsselfenster erkannt, aber hinzugefügte Unteransichten werden nicht angezeigt.
Beebcon
topWindow Wert ist nicht korrekt im Fall von iOS 8
Mehul Thakkar
11

Tatsächlich kann Ihre Anwendung mehr als ein UIWindow enthalten. Wenn beispielsweise eine Tastatur auf dem Bildschirm angezeigt [[UIApplication sharedApplication] windows]wird, enthält sie mindestens zwei Fenster (Ihr Tastenfenster und das Tastaturfenster).

Wenn Sie also möchten, dass Ihre Ansicht auf beiden angezeigt wird, müssen Sie Folgendes tun:

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

(Angenommen, lastObject enthält das Fenster mit der höchsten windowLevel-Priorität).

Lorean
quelle
[[[UIApplication sharedApplication] windows] lastObject] Wert nicht korrekt im Fall von ios 8
Mehul Thakkar
Ich habe damit begonnen, aber es scheint, dass manchmal das Tastaturfenster existiert, aber nicht angezeigt wird, und dann wird meine Ansicht überhaupt nicht angezeigt!
Teradyl
3

Ich bleibe bei der Frage, wie der Titel sagt, und nicht bei der Diskussion. Welche Ansicht ist an einem bestimmten Punkt von oben sichtbar?

@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

Kann direkt mit jedem UIWindow als Empfänger oder jedem UIView als Empfänger verwendet werden.

hfossli
quelle
topWindow gibt im Fall von iOS 8 einen falschen Wert. Können Sie bitte Ihre Antwort für iOS 8
aktualisieren
1
Ich habe keine Zeit, es herauszufinden. In diesem Fall können Sie diesen Beitrag aktualisieren.
Hfossli
1

Wenn Sie eine Ladeansicht hinzufügen (z. B. eine Aktivitätsindikatoransicht), stellen Sie sicher, dass Sie ein Objekt der UIWindow-Klasse haben. Wenn Sie kurz vor dem Anzeigen Ihrer Ladeansicht ein Aktionsblatt anzeigen, keyWindowist dies das UIActionSheetund nicht das UIWindow. Und da das Aktionsblatt verschwindet, verschwindet auch die Ladeansicht. Oder das hat mir Probleme bereitet.

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
quelle
1

Wenn Ihre Anwendung nur im Hochformat funktioniert, reicht dies aus:

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

Und Ihre Ansicht wird nicht über Tastatur und Statusleiste angezeigt.

Wenn Sie eine Ansicht von oben über die Tastatur oder die Statusleiste erhalten möchten oder die Ansicht von oben mit Geräten korrekt gedreht werden kann, versuchen Sie Folgendes:

https://github.com/HarrisonXi/TopmostView

Es unterstützt iOS7 / 8/9.

Harrison Xi
quelle
0

Verwenden Sie einfach diesen Code, wenn Sie eine Ansicht über alles auf dem Bildschirm hinzufügen möchten.

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

Versuche dies

UIWindow *window = [[[UIApplication sharedApplication] windows] lastObject];
Chandramani
quelle
Informationen zur Windows-Eigenschaft: "Diese Eigenschaft enthält ein Array von NSWindow-Objekten, die allen derzeit für die App vorhandenen Fenstern entsprechen. Das Array enthält alle Onscreen- und Offscreen-Fenster, unabhängig davon, ob sie in einem beliebigen Bereich sichtbar sind oder nicht. Es gibt keine Garantie für die Reihenfolge der Fenster im Array. "
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
quelle