Получение ссылки на самый верхний вид / окно в приложении iOS
Я создаю многоразовую платформу для отображения уведомлений в приложении iOS. Мне бы хотелось, чтобы представления уведомлений добавлялись поверх всего остального в приложении, что-то вроде UIAlertView. Когда я запускаю менеджер, который прослушивает события NSNotification и добавляет представления в ответ, мне нужно получить ссылку на самый верхний вид в приложении. Вот что у меня на данный момент:
_topView = [[[[UIApplication sharedApplication] keyWindow] subviews] lastObject];
Будет ли это работать для любого приложения iOS или это более безопасный / лучший способ получить вид сверху?
9 ответов
Обычно это дает вам вид сверху, но нет никакой гарантии, что он виден пользователю. Он может быть вне экрана, иметь альфа 0,0 или иметь размер 0x0, например.
Может также случиться так, что у keyWindow нет подпредставлений, поэтому вам, вероятно, следует сначала проверить это. Это было бы необычно, но это не невозможно.
UIWindow является подклассом UIView, поэтому, если вы хотите, чтобы ваше уведомление было видимым для пользователя, вы можете добавить его непосредственно в keyWindow, используя addSubview:
и он сразу станет самым популярным. Я не уверен, что это то, что вы хотите сделать, хотя. (Исходя из вашего вопроса, похоже, вы уже знаете это.)
Всякий раз, когда я хочу отобразить какое-либо наложение поверх всего остального, я просто добавляю его поверх окна приложения напрямую:
[[[UIApplication sharedApplication] keyWindow] addSubview:someView]
Есть две части проблемы: верхнее окно, вид сверху на верхнее окно.
Все существующие ответы не попали в верхнюю часть окна. Но [[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];
На самом деле в вашем приложении может быть более одного 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.
Попробуй это
UIWindow *window = [[[UIApplication sharedApplication] windows] lastObject];
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;
}
}
}
Просто используйте этот код, если вы хотите добавить вид выше всего на экране.
[[UIApplication sharedApplication].keyWindow addSubView: yourView];