98

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

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

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

4

10 回答 10

113

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

[[[UIApplication sharedApplication] keyWindow] addSubview:someView]
于 2010-10-02T00:29:41.393 回答
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];
    
于 2011-11-08T04:03:22.793 回答
36

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

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

UIWindow 是 UIView 的子类,所以如果你想确保你的通知对用户可见,你可以直接将它添加到 keyWindow 中addSubview:,它会立即成为最顶层的视图。我不确定这是否是你想要做的。(根据您的问题,您似乎已经知道这一点。)

于 2010-10-01T22:41:23.173 回答
11

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

因此,如果您希望您的视图出现在它们之上,那么您必须执行以下操作:

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

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

于 2012-02-28T21:32:01.467 回答
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 作为接收器一起使用。

于 2013-02-06T18:25:26.943 回答
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;
        }
    }
}
于 2015-11-03T14:55:17.737 回答
1

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

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

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

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

https://github.com/HarrisonXi/TopmostView

它支持iOS7/8/9。

于 2016-02-25T03:16:59.320 回答
0

试试这个

UIWindow *window = [[[UIApplication sharedApplication] windows] lastObject];
于 2016-08-18T09:02:20.157 回答
0

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

[[UIApplication sharedApplication].keyWindow addSubView: yourView];
于 2017-03-20T11:16:57.247 回答
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;
        }
    }
}
于 2020-01-24T12:01:27.427 回答