Подтвердить что ты не робот

UINavigationController и авторотация

У меня есть UIViewController, который возвращает YES в shouldAutorotateToInterfaceOrientation: для UIDeviceOrientationPortrait и NO для всего остального. С этим представлением в верхней части стека я использую pushViewController:animated:, чтобы нажать новый UIViewController. Новый контроллер возвращает YES для чего-либо в shouldAutorotateToInterfaceOrientation:.

Первый вид отказывается вращаться (как и ожидалось). Как только второе представление будет нажато, пользователь может повернуть устройство, и пользовательский интерфейс будет вращаться (также как и ожидалось). Если второй вид находится в ландшафтном режиме, и пользователь нажимает кнопку "Назад" (которая вызывает popViewControllerAnimated:), первое представление будет повернуто (неожиданно!).

Если пользователь поворачивает устройство обратно в портретную ориентацию, вид будет вращаться, а затем застрять в портретном режиме, как и раньше. Это работает, но это уродливо для пользователя, пока они не вернутся назад. Поэтому я ищу способ сделать это представление в портретном режиме.

Единственным обходным решением, которое я нашел до сих пор, является использование -[UIDevice setOrientation:], которое выдает предупреждение (orientation доступно только для чтения), но работает, поскольку оно фактически определено. Это огромный взлом, и я бы хотел найти реальное решение. В поисках реального решения я приложил GDB к приложению "Фотографии" (MobileSlideshow.app) и обнаружил, что он также использует -[UIDevice setOrientation:]. Являюсь внутренним приложением, хотя, я думаю, у них разные правила.

Есть ли правильный способ достижения ожидаемого поведения авторотации?

4b9b3361

Ответ 1

Я собирался сказать вам, что, вероятно, нет способа, но потом я подумал. Было бы трудно получить право, но вы могли бы заставить его работать, если бы вы использовали два отдельных UINavigationController s: один, который управляет корневым представлением и запрещает ротацию, и один для дочерних просмотров, который позволяет это. Вы должны вручную обработать переход к корневому контроллеру и дочернему контроллеру и обратно.

Вам нужно будет исправить дочерний навигационный контроллер, чтобы иметь правильную кнопку возврата. И, конечно же, вам придется обращаться к кнопке "Назад". Вам, вероятно, придется использовать манекен UINavigationBar, чтобы сделать анимацию от одного контроллера навигации до следующего, чтобы переход выглядел правильно. Вам также нужно будет анимировать переход "push" между навигационными контроллерами, что может потребовать немного настройки, чтобы заставить его выглядеть правильно. Вы должны:

  • Настройте фиктивную навигационную панель, чтобы точно соответствовать исходящему навигационному контроллеру, и поместите его прямо поверх панели навигационного контроллера. (Вы можете скопировать конфигурацию текущего контроллера представления UINavigationItem и нажать его)
  • Поместите новый контроллер навигации вне экрана на правый край.
  • Анимация движения кадров нового и старого контроллеров справа налево
  • Создайте копию UINavigationItem для контроллера входящего представления и надавите на манекенную панель навигации
  • Когда анимация завершается, удалите фиктивный UINavigationBar из представления, а также отправляющий навигационный контроллер.

Все это большая работа, но если вы очень умны (и очень цепкие), вы можете заставить ее работать. Я бы хотел увидеть результат!

Тем не менее, вам может быть лучше использовать setOrientation: и рискнуть с помощью процесса утверждения App Store; -)

Ответ 2

Правовая альтернатива UIDevice setOrientation следующая:

UIWindow* window = UIApplication.sharedApplication.keyWindow;
UIView* view = [window.subviews objectAtIndex:0];
[view removeFromSuperview];
[window addSubview:view];

Это заставляет текущий контроллер просмотра оценивать его ориентацию, вызывая toAutorotateToInterfaceOrientation и отказываясь от любых запрещенных ориентаций.

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

- (void)popBack
{
    [self.navigationController popToRootViewControllerAnimated:YES]; 
}

- (IBAction)backPressed:(id)sender
{   
    portraitOrientationPermitted = NO;

    // Force the framework to re-evaluate the interface orientation.
    UIWindow* window = UIApplication.sharedApplication.keyWindow;
    UIView* view = [window.subviews objectAtIndex:0];
    [view removeFromSuperview];
    [window addSubview:view];

    [self performSelector:@selector(popBack) withObject:nil afterDelay:0.8];

    portraitOrientationPermitted = YES;
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return portraitOrientationPermitted || 
        UIInterfaceOrientationIsLandscape(interfaceOrientation);
}

Ответ 3

Это был старый пост, но поскольку он не был решен. Я хотел бы поделиться своим решением, для любых других, у кого может быть головная боль.

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

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

Решение. Убийца должен запретить вращение в viewWillAppear и представить и отклонить modalViewController без анимации.

  • AppViewController добавляется в окно как хост viewController, то есть rooter, чем RootViewController;
  • В appViewController добавлен навигационный контроллер, с делегат установлен в appViewController;
  • В AppViewController


- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    if (interfaceOrientation == UIInterfaceOrientationPortrait) return YES;
    return canRotate;
}


- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
    [viewController viewDidAppear:animated];
    canRotate = ([navigationController.topViewController isKindOfClass:[MyRotatable class]]);
}


- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
    [viewController viewWillAppear:animated];
    if (![navigationController.topViewController isKindOfClass:[MyRotatable class]]) {
        canRotate = NO;
        UIViewController * blanck = [[UIViewController alloc] initWithNibName:nil bundle:nil];
        [self presentModalViewController:blanck animated:NO];
        [self dismissModalViewControllerAnimated:NO];
        [blanck release];
    }
}

Ответ 4

iOS 5 добавляет + [UIViewController tryRotationToDeviceOrientation], который решает проблему для меня.

Ответ 5

Попробуйте еще раз с ОС 3.0 (теперь мы можем поговорить об этом). Два особых случая этого были разработаны в версии 3.0:

  • Представление портретного модального вида над пейзажным видом и наоборот. Примером является поведение pre-3.0 в Mail для просмотра вложений. Вы можете повернуть в альбомную область для PDF файла и вернуть портретный вид сообщения, когда вы отклонили представление вложения. (Теперь, когда у нас есть представление о ландшафтных сообщениях в 3.0, это поведение, похоже, исчезло).

  • Смешивание ориентаций в стеке просмотра и возврат правильной ориентации назад, когда вы поместите стек. Примером может служить переход между просмотром фильмов и просмотром видео в приложении YouTube.

Кажется, что были некоторые острые эстетические проблемы того, как должны выглядеть переходы по умолчанию для всех перестановок. Есть некоторые странные элементы, если вы смотрите в slo-mo, но он бьет в обратном направлении в свой собственный код.

Ответ 6

Я нашел хороший обходной путь для этой проблемы. Подсказка состоит в том, чтобы поддерживать все ориентации для всех видов в UINavigationController.

У меня есть 2 представления в контроллере. Root view поддерживает только LandscapeRight, а второй поддерживает как LandscapeRight, так и Portrait.

Второй вид shouldAutorotateToInterfaceOrientation выглядит как обычно:

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return (interfaceOrientation == UIInterfaceOrientationLandscapeRight) ||
           (interfaceOrientation == UIInterfaceOrientationPortrait);
}

Обходной путь содержится в корневом представлении Теперь корневой вид вращается в терминах кода, но пользователь не видит его.

//auxiliary function
-(void) fixOrientation:(UIInterfaceOrientation)orientation
{
    if (orientation == UIInterfaceOrientationPortrait)
        self.view.transform = CGAffineTransformMakeRotation(M_PI_2);
    else if (orientation == UIInterfaceOrientationLandscapeRight)
        self.view.transform = CGAffineTransformMakeRotation(0);
}

-(void) viewWillAppear:(BOOL)animated
{
    [self fixOrientation:[[UIApplication sharedApplication] statusBarOrientation]];
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    [self fixOrientation:interfaceOrientation];
    //notice, that both orientations are accepted
    return (interfaceOrientation == UIInterfaceOrientationLandscapeRight) ||
           (interfaceOrientation == UIInterfaceOrientationPortrait);
}

//these two functions helps to avoid blinking
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
    [UIView setAnimationsEnabled:NO]; // disable animations temporarily

}

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
    [UIView setAnimationsEnabled:YES]; // rotation finished, re-enable them
}

Ответ 7

Таким образом, эта досадная ошибка приходит очень далеко от iOS 1 до iOS 4.

Я считаю, что лучшее решение у нас есть, чтобы дублировать эту ошибку, и пусть Apple знает, что мы действительно хотим, чтобы она была исправлена. Я только что сообщил об этом снова под идентификатором ошибки 8478525.