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

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

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

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

Ответ 1

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

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

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

Ответ 2

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

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

Ответ 3

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

Все существующие ответы пропустили верхнюю часть окна. Но [[UIApplication sharedApplication] keyWindow] не гарантированно будет верхним окном.

  • Верхнее окно. Очень маловероятно, что для приложения будет два окна с одним и тем же windowLevel, поэтому мы можем отсортировать все окна с помощью windowLevel и получить самый верхний.

    UIWindow *topWindow = [[[UIApplication sharedApplication].windows sortedArrayUsingComparator:^NSComparisonResult(UIWindow *win1, UIWindow *win2) {
        return win1.windowLevel - win2.windowLevel;
    }] lastObject];
    
  • Вид сверху в верхнем окне. Просто чтобы быть полным. Как уже указывалось в вопросе:

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

Ответ 4

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

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

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

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

Ответ 5

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

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

Ответ 6

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

Ответ 7

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

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

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

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

https://github.com/HarrisonXi/TopmostView

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

Ответ 8

попробуйте это

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

Ответ 9

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

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