Анимации UIViewController ждут завершения анимации

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

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

Предупреждение: Попытка представить, чье представление не находится в иерархии окон!`.

Каков наилучший способ дождаться окончания анимации, а затем изменить вращение?

Это то, что я использую в Контроллере представления представления:

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    [self beginDeviceOrientationListener];
}

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

- (void)beginDeviceOrientationListener
{
    [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(orientationChanged:) name:UIDeviceOrientationDidChangeNotification object:[UIDevice currentDevice]];
}

- (void)orientationChanged:(NSNotification *)notification
{
    UIDevice *device = notification.object;
    switch (device.orientation)
    {
        case UIDeviceOrientationLandscapeLeft:
        case UIDeviceOrientationLandscapeRight:
        {
            TheViewControllerToPresent *viewController = [[TheViewControllerToPresent alloc] init];
            [self presentViewController:viewController animated:YES completion:nil];
            [[UIDevice currentDevice] setValue:[NSNumber numberWithInteger:UIInterfaceOrientationLandscapeRight] forKey:@"orientation"];
            [[UIApplication sharedApplication] setStatusBarOrientation:[[[UIDevice currentDevice] valueForKey:@"orientation"] integerValue] animated:YES];
            [[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationSlide];
        }
            break;

        default:
            break;
    }
}

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

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    [self beginDeviceOrientationListener];
}

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

- (void)beginDeviceOrientationListener
{
    [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(orientationChanged:) name:UIDeviceOrientationDidChangeNotification object:[UIDevice currentDevice]];
}

- (void)orientationChanged:(NSNotification *)notification
{
    UIDevice *device = notification.object;
    switch (device.orientation)
    {
        case UIDeviceOrientationPortrait:
        {
            [self dismissViewControllerAnimated:YES completion:nil];
            [[UIDevice currentDevice] setValue:[NSNumber numberWithInteger:UIInterfaceOrientationPortrait] forKey:@"orientation"];
            [[UIApplication sharedApplication] setStatusBarOrientation:[[[UIDevice currentDevice] valueForKey:@"orientation"] integerValue] animated:YES];
        }
            break;
        default:
            break;
    }
}

person Yossi Tsafar    schedule 07.05.2015    source источник
comment
Почему вы устанавливаете [UIDevice orientation]? что за ужасный хак?   -  person Sulthan    schedule 07.05.2015
comment
Ну, это на самом деле работает довольно хорошо, зачем хак? что я должен использовать вместо этого?   -  person Yossi Tsafar    schedule 07.05.2015
comment
[UIDevice orientation] по какой-то причине доступен только для чтения. Хак заключается в том, что вы обращаетесь к нему с помощью отражения (setValue:), минуя статус readonly. Если вы не хотите использовать поддержку вращения по умолчанию, которую имеют контроллеры, вы можете просто использовать transform в представлении представленного контроллера.   -  person Sulthan    schedule 07.05.2015


Ответы (1)


Самое простое решение, которое я использую сам, — запретить пользователю вносить какие-либо изменения во время выполнения анимации.

Это делается путем добавления следующего кода при запуске анимации:

[[UIApplication sharedApplication] beginIgnoringInteractionEvents];
[[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications];

и обработчику завершения:

[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[UIApplication sharedApplication] endIgnoringInteractionEvents];

Пользователь может вращать устройство, но события не будут генерироваться во время анимации, поэтому анимации не будут конфликтовать. Однако, когда анимация заканчивается, вы должны явно проверить ориентацию:

UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];

чтобы увидеть, следует ли вам запускать другую анимацию или нет.

person Sulthan    schedule 07.05.2015