Просмотр поверх всего: подвид UIWindow VS подвид UIViewController

Чтобы сделать UIView поверх всех представлений, подобных поведению UIAlertView, лучший способ, который я вижу, — это поместить его в окно приложения, то есть добавив представление в:

[[[UIApplication sharedApplication] delegate] window]

Тем не менее, недостатком, который я обнаружил, является то, что он не принимает вращение автоматически. Для того, чтобы он начал ротацию, лучше всего добавить представление в текущий AppDelegate window.navigationController/rootViewController, однако при этом он больше не будет поверх всего. Скажем, когда отображается представление и есть всплывающее окно modalViewController, модальное представление обязательно закроет представление.

Мой вопрос: можно ли добавить подвид в самое верхнее окно и поддерживать ориентацию? Нужны ли мне в этом случае два комплекта файлов Nib? Как UIAlertView волшебство сочетает поддержку самого верхнего положения и ориентации? Когда я смотрю на UIAlertView.h, я вижу, что на самом деле 2 UIWindows, один называется originalWindow, а другой называется dimWindow. Есть ли место, где я могу найти исходный код для UIAlertView.m?


person Zian Chen    schedule 08.01.2012    source источник


Ответы (3)


Лучшее решение, которое я нашел, - создать прозрачное представление контейнера, добавить этот контейнер в окно и поместить оповещение внутри контейнера. Затем вы можете зарегистрироваться для UIApplicationWillChangeStatusBarOrientationNotification, чтобы получать события вращения и преобразовывать контейнер; это позволяет самостоятельно манипулировать рамкой оповещения:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 
{
     ...
     self.container = [[UIView alloc] initWithFrame:self.window.bounds];
     [self.container addSubview:self.alertView];
     [self.window addSubview:self.container];
     [[NSNotificationCenter defaultCenter] addObserver:self 
                                              selector:@selector(statusBarWillRotate:) 
                                                  name:UIApplicationWillChangeStatusBarOrientationNotification 
                                                object:nil];
     ...
}
...
- (void)statusBarWillRotate:(NSNotification *)theNotification
{
    CGFloat rotation = 0;
    switch ([notification[UIApplicationStatusBarOrientationUserInfoKey] intValue])
    {
        case UIInterfaceOrientationLandscapeLeft:
            rotation = -M_PI_2;
            break;
        case UIInterfaceOrientationLandscapeRight:
            rotation = M_PI_2;
            break;
        case UIInterfaceOrientationPortraitUpsideDown:
            rotation = M_PI;
            break;
        case UIInterfaceOrientationPortrait: 
        default:
            rotation = 0;
            break;
    }
    CGAffineTransform rotationTransform = CGAffineTransformMakeRotation(rotation);
    CGSize rotatedSize = CGRectApplyAffineTransform(self.window.bounds, rotationTransform).size;
    [UIView animationWithDuration:[UIApplication sharedApplication].statusBarOrientationAnimationDuration
                       animations:^{
         self.container.transform = rotationTransform;
         // Transform invalidates the frame, so use bounds/center
         self.container.bounds = CGRectMake(0, 0, rotatedSize.width, rotatedSize.height);
         self.container.center = CGPointMake(self.window.bounds.size.width / 2, self.window.bounds.size.height / 2);
     }];
}

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

Проблема с простым добавлением представления контроллера представления в окно заключается в том, что не гарантируется получение всех сообщений поворота и view[Will|Did][Disa|A]ppear:, если только оно не добавлено в иерархию контроллера представления через addChildViewController: на корневом контроллере представления.

person Austin    schedule 13.02.2013
comment
У меня проблема с этим решением. Я не могу регистрировать прикосновения в самом верхнем представлении контейнера. Например, UIButtons не работает. - person asendra; 01.12.2014
comment
@asendra Похоже, ваше окно не является ключевым окном, но вы можете опубликовать свой вопрос с более подробной информацией. - person Austin; 01.12.2014

Только первое подпредставление UIWindow получает информацию об изменении ориентации. (‹ iOS8)

Ваш вопрос очень похож на этот:

Несколько представлений в UIWindow

Как говорится в ответе, вы можете зарегистрироваться для получения уведомлений об изменении ориентации и таким образом обрабатывать ретрансляцию вашего «верхнего» подвида.

person occulus    schedule 08.01.2012
comment
Как зарегистрировать представление на устройстве для получения события вращения? Это не View Controller, поэтому для него нет функций делегата. Должен ли я поймать UIDeviceOrientationDidChangeNotification, чтобы изменить соответствующую ориентацию? - person Zian Chen; 08.01.2012

Вот возможное решение, я объясняю в несколько шагов.

  1. Начните с унаследованного класса UIViewController вместо UIView, скажем, класса с именем TopViewController, унаследованного UIViewController
  2. Поскольку вы не можете зарегистрировать поворот устройства/интерфейса в унаследованном классе UIView, класс TopViewController, унаследованный от UIViewController, имеет методы, отвечающие на события вращения просмотра. Теперь можно фиксировать события ротации.
  3. Наконец, вы можете поместить представление TopViewController поверх UIWindow, используя тот же метод, который вы упомянули в своем вопросе.

    [[[[UIApplication sharedApplication] delegate] window] addSubview:(TopViewController object).view];
    

Надеюсь это поможет.

person Rahul    schedule 07.02.2013
comment
Свойство представления UIViewController также не будет вращаться. К несчастью. developer.apple.com/library/ios/qa/qa1688/_index. html - person khunshan; 12.08.2014