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


97

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

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

这是否适用于任何iOS应用程序,或者是获取顶视图的更安全/更好的方法?

Answers:


36

通常,这将为您提供顶视图,但不能保证用户可以看到它。例如,它可能不在屏幕上,alpha值为0.0,或者大小可能为0x0。

也可能是keyWindow没有子视图,因此您应该首先进行测试。这将是不寻常的,但并非不可能。

UIWindow是UIView的子类,因此,如果要确保您的通知对用户可见,可以使用将其直接添加到keyWindow中addSubview:,它将立即成为最顶部的视图。我不确定这是否是您要执行的操作。(根据您的问题,看起来您已经知道这一点。)


111

每当我想在其他所有内容上显示一些覆盖时,我都将其直接添加到“应用程序窗口”的顶部:

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

9
只有在纵向工程..你得关心旋转等这样的:stackoverflow.com/questions/2508630/...
hfossli

1
我进入(null)我的iOS-8应用程序(故事板问题?)有什么提示吗?谢谢!(注意:(null)在时间viewDidLoadviewWillAppear:时间都返回。 viewDidAppear:为时已晚
。– Olie

当我们提供视图控制器时,此功能有效吗?添加的子视图会显示在所显示的视图控制器上吗?
Pratyusha Terli 2015年

这不会超过键盘,但是lastObject会。
2015年

该框架可以在所有方向上工作。并且将超过键盘。github.com/HarrisonXi/TopmostView
Harrison Xi

47

问题有两个部分:顶部窗口,顶部窗口的顶视图。

所有现有答案都错过了顶部窗口部分。但[[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];

5
UIAlertViews发挥作用后,该解决方案对我来说不再起作用-它们似乎留下了空空的UIWindow,所以我又回到了topView = [[[[UIApplication sharedApplication] keyWindow] subviews] lastObject];
Gereon

4
如果键盘向上,则此功能不起作用。因为那将是最顶层的窗口

@Gereon,请确保您没有持有对任何UIAlertView的强烈引用。如果不是弱引用,则UIAlertOverlayWindow仍将位于层次结构中并被识别为关键窗口,但是添加到其中的子视图将不会显示。
beebcon 2014年

topWindow值不正确在IOS 8的情况下
MEHUL THAKKAR

11

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

因此,如果您希望视图同时出现在两者之上,则必须执行以下操作:

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

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


[[[[UIApplication sharedApplication] windows] lastObject]值不适用于ios 8
Mehul Thakkar,

我开始使用它,但是似乎有时键盘窗口存在但没有显示,然后我的视图完全不显示!
teradyl

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作为接收器一起使用。


topWindow在iOS 8的情况下给出错误的值,你能请更新您的iOS 8回答
MEHUL THAKKAR

1
我没有时间找出答案。如果您愿意,您可以自由更新此帖子。
hfossli 2014年

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

1

如果您的应用程序仅以纵向方向工作,那就足够了:

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

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

如果您想通过键盘或状态栏获得最高的视图,或者您希望最高的视图可以随设备正确旋转,请尝试以下框架:

https://github.com/HarrisonXi/TopmostView

它支持iOS7 / 8/9。


0

如果要在屏幕上所有内容的上方添加视图,只需使用此代码。

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

0

试试这个

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

关于windows属性:“此属性包含与应用程序当前所有现有窗口相对应的NSWindow对象数组。该数组包括所有屏幕上和屏幕外窗口,无论它们在任何空间上均可见。无法保证顺序阵列中的窗口。”
史蒂芬·费舍尔

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;
        }
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.