获取对iOS应用程序中最顶层视图/窗口的引用

时间:2010-10-01 22:22:26

标签: objective-c uiview ios uiwindow

我正在创建一个可重用的框架,用于在iOS应用程序中显示通知。我希望将通知视图添加到应用程序中其他所有内容的顶部,有点像UIAlertView。当我初始化侦听NSNotification事件并在响应中添加视图的管理器时,我需要获得对应用程序中最顶层视图的引用。这就是我现在所拥有的:

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

这适用于任何iOS应用程序,还是更安全/更好的方式来获得顶视图?

10 个答案:

答案 0 :(得分:109)

每当我想在其他所有内容上显示一些叠加层时,我只是将它直接添加到应用程序窗口的顶部:

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

答案 1 :(得分:46)

问题分为两部分:顶部窗口,顶部窗口顶视图。

所有现有答案都错过了顶部窗口部分。但[[UIApplication sharedApplication] keyWindow]并不能保证成为最佳窗口。

  1. 顶部窗口。应用程序中存在两个具有相同windowLevel共存的窗口是不太可能的,因此我们可以按windowLevel对所有窗口进行排序并获得最顶层的窗口。

    UIWindow *topWindow = [[[UIApplication sharedApplication].windows sortedArrayUsingComparator:^NSComparisonResult(UIWindow *win1, UIWindow *win2) {
        return win1.windowLevel - win2.windowLevel;
    }] lastObject];
    
  2. 顶部窗口的顶视图。只是为了完整。正如问题中已经指出的那样:

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

答案 2 :(得分:36)

通常这会给你顶视图,但不能保证它对用户可见。它可能在屏幕外,具有0.0的alpha值,或者例如可以具有0x0的大小。

也可能是keyWindow没有子视图,所以你应该先测试一下。这将是不寻常的,但这并非不可能。

UIWindow是UIView的子类,因此如果您想确保用户可以看到您的通知,可以使用addSubview:将其直接添加到keyWindow,它将立即成为最顶层的视图。我不确定这是不是你想要做的。 (根据你的问题,看起来你已经知道了。)

答案 3 :(得分:11)

实际上,您的应用程序中可能有多个UIWindow。例如,如果键盘在屏幕上,则[[UIApplication sharedApplication] windows]将包含至少两个窗口(您的键窗口和键盘窗口)。

因此,如果您希望您的视图显示在两个视图的顶部,那么您必须执行以下操作:

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

(假设lastObject包含windowLevel优先级最高的窗口)。

答案 4 :(得分:3)

我坚持这个标题,而不是讨论。在任何给定点上哪个视图最顶部?

@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

可以直接与任何UIWindow一起用作接收器或任何UIView作为接收器。

答案 5 :(得分:1)

如果要添加加载视图(例如活动指示器视图),请确保您具有UIWindow类的对象。如果您在显示加载视图之前显示操作表,keyWindow将是UIActionSheet而不是UIWindow。由于操作表将消失,加载视图将随之消失。或者这就是导致我出现问题的原因。

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

答案 6 :(得分:1)

如果您的应用程序仅适用于纵向,这就足够了:

str.contains()

您的视图不会显示在键盘和状态栏上。

如果您希望通过键盘或状态栏获得最顶层视图,或者您希望最顶层视图可以使用设备正确旋转,请尝试此框架:

https://github.com/HarrisonXi/TopmostView

它支持iOS7 / 8/9。

答案 7 :(得分:0)

试试这个

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

答案 8 :(得分:0)

如果您想在屏幕上方添加一个视图,请使用此代码。

pop(valuetoremove,myarray);

答案 9 :(得分: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;
        }
    }
}