Почему зарегистрированный UIDeviceOrientationDidChangeNotification не всегда вызывается? - PullRequest
3 голосов
/ 10 февраля 2011

Я написал простой контроллер вида, который показывает модальное всплывающее диалоговое окно. Поскольку он создает новое окно для наложения на весь экран, я добавил наблюдателя UIDeviceOrientationDidChangeNotification, который выясняет, как вращать представление, используя CGAffineTransformMakeRotation(). И это похоже на работу.

В основном. Недостатком является то, что я могу играть с ориентацией и заставить ее заканчивать вверх или вверх ногами. Проверьте это видео 18-х годов , демонстрирующее проблему. При отладке кажется, что иногда уведомление не срабатывает или, по крайней мере, не вызывает метод обратного вызова, который его прослушивает.

Я, должно быть, что-то упускаю, потому что, если вы снова посмотрите видео, вы заметите, что представление вращается правильно. Этим управляет -willAnimateRotationToInterfaceOrientation:duration:. Как получается, что метод контроллера iOS всегда вызывается правильно (предположительно управляется наблюдателем UIDeviceOrientationDidChangeNotification в UIViewController), но мой собственный код - нет?

Ответы [ 3 ]

12 голосов
/ 17 февраля 2011

Я использовал UIApplicationWillChangeStatusBarOrientationNotification в Кобо, что, кажется, делает трюк.Это намного надежнее, потому что вы хотите соответствовать выбранной ориентации пользовательского интерфейса, а не обязательно там, где находится устройство.Другими словами, вы можете позволить Apple решить, когда вам следует переориентировать свое окно.

Вот несколько фрагментов из всплывающего диалогового кода приложения Kobo на iPad (UIAlertView на iPad выглядит немного странно, поэтому Ричард Пеннер написал лучше).После нескольких сбоев с более свежими версиями iOS и необходимости отображать эти диалоги в разных ситуациях (все вокруг ориентации IIRC), мне пришлось настроить его так, чтобы он находился прямо внутри UIWindow, что означало дублирование всей логики преобразования ориентации.

Этот код взят из UIViewController, чье представление перетаскивается прямо в текущее окно.

- (void) presentDialogWindow
{
    // register for orientation change notification
    [[NSNotificationCenter defaultCenter] addObserver: self
                                             selector: @selector(orientationWillChange:)
                                                 name: UIApplicationWillChangeStatusBarOrientationNotification
                                               object: nil];
    [[NSNotificationCenter defaultCenter] addObserver: self
                                             selector: @selector(orientationDidChange:)
                                                 name: UIApplicationDidChangeStatusBarOrientationNotification
                                               object: nil];
}

- (void) orientationWillChange: (NSNotification *) note
{
    UIInterfaceOrientation current = [[UIApplication sharedApplication] statusBarOrientation];
    UIInterfaceOrientation orientation = [[[note userInfo] objectForKey: UIApplicationStatusBarOrientationUserInfoKey] integerValue];
    if ( [self shouldAutorotateToInterfaceOrientation: orientation] == NO )
        return;

    if ( current == orientation )
        return;

    // direction and angle
    CGFloat angle = 0.0;
    switch ( current )
    {
        case UIInterfaceOrientationPortrait:
        {
            switch ( orientation )
            {
                case UIInterfaceOrientationPortraitUpsideDown:
                    angle = (CGFloat)M_PI;  // 180.0*M_PI/180.0 == M_PI
                    break;
                case UIInterfaceOrientationLandscapeLeft:
                    angle = (CGFloat)(M_PI*-90.0)/180.0;
                    break;
                case UIInterfaceOrientationLandscapeRight:
                    angle = (CGFloat)(M_PI*90.0)/180.0;
                    break;
                default:
                    return;
            }
            break;
        }
        case UIInterfaceOrientationPortraitUpsideDown:
        {
            switch ( orientation )
            {
                case UIInterfaceOrientationPortrait:
                    angle = (CGFloat)M_PI;  // 180.0*M_PI/180.0 == M_PI
                    break;
                case UIInterfaceOrientationLandscapeLeft:
                    angle = (CGFloat)(M_PI*90.0)/180.0;
                    break;
                case UIInterfaceOrientationLandscapeRight:
                    angle = (CGFloat)(M_PI*-90.0)/180.0;
                    break;
                default:
                    return;
            }
            break;
        }
        case UIInterfaceOrientationLandscapeLeft:
        {
            switch ( orientation )
            {
                case UIInterfaceOrientationLandscapeRight:
                    angle = (CGFloat)M_PI;  // 180.0*M_PI/180.0 == M_PI
                    break;
                case UIInterfaceOrientationPortraitUpsideDown:
                    angle = (CGFloat)(M_PI*-90.0)/180.0;
                    break;
                case UIInterfaceOrientationPortrait:
                    angle = (CGFloat)(M_PI*90.0)/180.0;
                    break;
                default:
                    return;
            }
            break;
        }
        case UIInterfaceOrientationLandscapeRight:
        {
            switch ( orientation )
            {
                case UIInterfaceOrientationLandscapeLeft:
                    angle = (CGFloat)M_PI;  // 180.0*M_PI/180.0 == M_PI
                    break;
                case UIInterfaceOrientationPortrait:
                    angle = (CGFloat)(M_PI*-90.0)/180.0;
                    break;
                case UIInterfaceOrientationPortraitUpsideDown:
                    angle = (CGFloat)(M_PI*90.0)/180.0;
                    break;
                default:
                    return;
            }
            break;
        }
    }

    CGAffineTransform rotation = CGAffineTransformMakeRotation( angle );

    [UIView beginAnimations: @"" context: NULL];
    [UIView setAnimationDuration: 0.4];
    self.view.transform = CGAffineTransformConcat(self.view.transform, rotation);
    [UIView commitAnimations];
}

- (void) orientationDidChange: (NSNotification *) note
{
    UIInterfaceOrientation orientation = [[[note userInfo] objectForKey: UIApplicationStatusBarOrientationUserInfoKey] integerValue];
    if ( [self shouldAutorotateToInterfaceOrientation: [[UIApplication sharedApplication] statusBarOrientation]] == NO )
        return;

    self.view.frame = [[UIScreen mainScreen] applicationFrame];

    [self didRotateFromInterfaceOrientation: orientation];
}

Мы используем его, загружая этот контроллер представления, добавляя его представление в окно, затем вызывая -presentModalDialog, как:

UIView *window = [[UIApplication sharedApplication] keyWindow];
[window addSubview: theController.view];
[theController presentDialogWindow];

Я собираюсь сделать простой подкласс UIViewController, чтобы в скором времени реализовать скромные биты, и опубликую его на моем аккаунте github.Когда я это сделаю, я отредактирую это и ссылку на него.

0 голосов
/ 14 февраля 2011

Я исправил эту проблему, исключив использование UIDeviceOrientationDidChangeNotification. Вместо этого я создаю подкласс UIViewController и задаю для настраиваемого представления его вид. Затем он просто реализует -willAnimateRotationToInterfaceOrientation:duration:, чтобы сделать вращение. Это на самом деле менее привередливый код для меня. Тем не менее, это похоже на хак, чтобы создать произвольный UIViewController просто для того, чтобы воспользоваться преимуществами автоповорота в том, что по сути является клоном presentModalViewController. Похоже, UIDeviceOrientationDidChangeNotification должен работать должным образом. Я ожидаю, что есть способ заставить его вести себя, но я еще не нашел его. : - (

0 голосов
/ 10 февраля 2011

UIDeviceOrientation имеет несколько значений, которые не отображаются на UIInterfaceOrientation:

UIDeviceOrientationUnknown,
UIDeviceOrientationFaceUp,              // Device oriented flat, face up
UIDeviceOrientationFaceDown             // Device oriented flat, face down

Возможно ли, что обратный вызов уведомления срабатывает, но эти случаи не обрабатываются?

Если контроллер представления имеет доступ к окну - он может перенаправить обратный вызов didRotate в окно вручную.

...