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

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

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

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


person typeoneerror    schedule 01.10.2010    source источник


Ответы (9)


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

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

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

person Kris Markel    schedule 01.10.2010

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

[[[UIApplication sharedApplication] keyWindow] addSubview:someView]
person samvermette    schedule 02.10.2010
comment
Работает только в портретной ориентации. Вам нужно позаботиться о поворотах и ​​т. Д., Например: stackoverflow.com/questions/2508630/ - person hfossli; 06.02.2013
comment
Я получаю (null) в моем приложении iOS-8 (проблема с раскадровкой?) Есть подсказки? Спасибо! (Примечание: (null) возвращается как в viewDidLoad, так и в viewWillAppear: время. viewDidAppear: уже слишком поздно. - person Olie; 26.02.2015
comment
это работает, когда мы представляем контроллер представления ?. Будет ли добавленное подпредставление отображаться поверх представленного контроллера представления? - person Pratyusha Terli; 24.03.2015
comment
Это не будет выходить за рамки клавиатуры, хотя lastObject делает. - person Ser Pounce; 13.05.2015
comment
Эта структура может работать во всех ориентациях. И пойдет над клавиатурой. github.com/HarrisonXi/TopmostView - person Harrison Xi; 25.02.2016

Проблема состоит из двух частей: верхнее окно, вид сверху на верхнее окно.

Все существующие ответы не попали в верхнюю часть окна. Но [[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];
    
person an0    schedule 08.11.2011
comment
Это решение перестало работать для меня, когда в игру вошли UIAlertViews - кажется, они оставляют пустое окно UIWindow, поэтому я вернулся к topView = [[[[UIApplication sharedApplication] keyWindow] subviews] lastObject]; - person Gereon; 05.01.2013
comment
Это не работает, если клавиатура поднята. Поскольку это будет самое верхнее окно - person entropy; 10.09.2013
comment
@Gereon, убедитесь, что у вас нет сильной ссылки на какой-либо UIAlertView. Если это не слабая ссылка, UIAlertOverlayWindow по-прежнему будет находиться в иерархии и распознаваться как ключевое окно, но добавленные к нему подпредставления не будут отображаться. - person beebcon; 04.07.2014
comment
Значение topWindow неверно в случае iOS 8 - person Mehul Thakkar; 01.12.2014

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

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

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

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

person lorean    schedule 28.02.2012
comment
[[[UIApplication sharedApplication] windows] lastObject] значение неверно в случае ios 8 - person Mehul Thakkar; 01.12.2014
comment
Я начал использовать это, но иногда кажется, что окно клавиатуры существует, но не отображается, а затем мое представление вообще не отображается! - person teradyl; 21.06.2016

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

@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 в качестве получателя.

person hfossli    schedule 06.02.2013
comment
topWindow дает неверное значение в случае iOS 8. Не могли бы вы обновить свой ответ для iOS 8? - person Mehul Thakkar; 01.12.2014
comment
У меня нет времени узнавать. Если вы это сделаете, вы можете обновить этот пост. - person hfossli; 01.12.2014

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

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

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

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

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

https://github.com/HarrisonXi/TopmostView

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

person Harrison Xi    schedule 25.02.2016

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

[[UIApplication sharedApplication].keyWindow addSubView: yourView];
person handiansom    schedule 20.03.2017

попробуй это

UIWindow *window = [[[UIApplication sharedApplication] windows] lastObject];
person Chandramani    schedule 18.08.2016
comment
О свойстве windows: это свойство содержит массив объектов NSWindow, соответствующих всем существующим в данный момент окнам для приложения. В массив входят все экранные и закадровые окна, независимо от того, видны они в каком-либо пространстве или нет. Нет никакой гарантии порядка окон в массиве. - person Steven Fisher; 27.03.2018