Получение ссылки на самый верхний Вид/Окно в приложении iOS


Я создаю многоразовую платформу для отображения уведомлений в приложении iOS. Я бы хотел, чтобы представления уведомлений добавлялись поверх всего остального в приложении, вроде UIAlertView. Когда я инициализирую менеджер, который слушает события NSNotification и добавляет представления в ответ, мне нужно получить ссылку на самый верхний вид в приложении. Вот что у меня есть на данный момент:

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

будет ли это работать для любого приложения iOS или их a более безопасный / лучший способ получить вид сверху?

9 94

9 ответов:

обычно это даст вам вид сверху, но нет никакой гарантии, что он виден пользователю. Он может быть вне экрана, иметь Альфа 0.0 или может иметь размер 0x0, например.

также может быть, что keyWindow не имеет подзадач, поэтому вы, вероятно, должны сначала проверить это. Это было бы необычно, но это не невозможно.

UIWindow-это подкласс UIView, поэтому если вы хотите убедиться, что ваше уведомление видно пользователю, вы можете добавить его непосредственно в keyWindow с помощью addSubview: и это будет мгновенно самый верхний вид. Я не уверен, если это то, что вы хотите сделать. (Судя по вашему вопросу, похоже, что вы уже знаете это.)

всякий раз, когда я хочу отобразить некоторое наложение поверх всего остального, я просто добавляю его поверх окна приложения напрямую:

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

есть две части проблемы: верхнем окне вид сверху на верхнее окно.

все существующие ответы пропустили верхнюю часть окна. Но [[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];
    

На самом деле может быть более одного UIWindow в вашем приложении. Например, если клавиатура находится на экране, то [[UIApplication sharedApplication] windows] будет содержать по крайней мере два окна (ваш ключ-окно и окно клавиатуры).

Так что если вы хотите, чтобы ваш вид появился на вершине обоих из них, то вы должны сделать что-то вроде:

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

(предполагая, что lastObject содержит окно с самым высоким приоритетом windowLevel).

Я придерживаюсь вопроса, как говорится в заголовке, а не в обсуждении. Какой вид сверху виден в любой заданной точке?

@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 в качестве приемника.

если вы добавляете вид нагрузки (вид активности, например), убедитесь, что у вас есть объект класса 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;
        }
    }
}

Если ваше приложение работает только в портретной ориентации, этого достаточно:

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

и ваш вид не будет отображаться на клавиатуре и в строке состояния.

Если вы хотите получить самый верхний вид, который над клавиатурой или строкой состояния, или вы хотите, чтобы самый верхний вид мог правильно вращаться с устройствами, попробуйте эту структуру:

https://github.com/HarrisonXi/TopmostView

Он поддерживает iOS7 / 8 / 9.

просто используйте этот код, если вы хотите добавить мнение выше всего на экране.

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

попробуй такое

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