Getting reference to the top-most view/window in iOS application

Solution 1:

Whenever I want to display some overlay on top of everything else, I just add it on top of the Application Window directly:

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

Solution 2:

There are two parts of the problem: Top window, top view on top window.

All the existing answers missed the top window part. But [[UIApplication sharedApplication] keyWindow] is not guaranteed to be the top window.

  1. Top window. It is very unlikely that there will be two windows with the same windowLevel coexist for an app, so we can sort all the windows by windowLevel and get the topmost one.

    UIWindow *topWindow = [[[UIApplication sharedApplication].windows sortedArrayUsingComparator:^NSComparisonResult(UIWindow *win1, UIWindow *win2) {
        return win1.windowLevel - win2.windowLevel;
    }] lastObject];
    
  2. Top view on top window. Just to be complete. As already pointed out in the question:

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

Solution 3:

Usually that will give you the top view, but there's no guarantee that it's visible to the user. It could be off the screen, have an alpha of 0.0, or could be have size of 0x0 for example.

It could also be that the keyWindow has no subviews, so you should probably test for that first. This would be unusual, but it's not impossible.

UIWindow is a subclass of UIView, so if you want to make sure your notification is visible to the user, you can add it directly to the keyWindow using addSubview: and it will instantly be the top most view. I'm not sure if this is what you're looking to do though. (Based on your question, it looks like you already know this.)

Solution 4:

Actually there could be more than one UIWindow in your application. For example, if a keyboard is on screen then [[UIApplication sharedApplication] windows] will contain at least two windows (your key-window and the keyboard window).

So if you want your view to appear ontop of both of them then you gotta do something like:

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

(Assuming lastObject contains the window with the highest windowLevel priority).

Solution 5:

I'm sticking to the question as the title states and not the discussion. Which view is top visible on any given point?

@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

Can be used directly with any UIWindow as receiver or any UIView as receiver.