UINavigationController и авторотация - PullRequest
24 голосов
/ 09 июня 2009

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

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

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

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

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

Ответы [ 7 ]

6 голосов
/ 15 ноября 2011

Юридическая альтернатива UIDevice setOrientation выглядит следующим образом:

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

Это вынуждает текущий контроллер вида оценить свою ориентацию, вызывая shouldAutorotateToInterfaceOrientation и отключаясь от любых запрещенных ориентаций.

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

- (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);
}
5 голосов
/ 18 марта 2011

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

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

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

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

  1. appViewController добавляется в окно в качестве хоста viewController, то есть рутер, чем RootViewController;
  2. В appViewController добавлен навигационный контроллер с делегат установлен в appViewController;
  3. В 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 голосов
/ 21 сентября 2012

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

1 голос
/ 08 августа 2012

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

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

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

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

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

//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
}
1 голос
/ 17 июня 2009

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

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

  2. Смешивание ориентаций внутри стека вида и возвращение правильной ориентации при извлечении стека. Примером является переход между просмотром таблицы фильмов и просмотром фильма в приложении YouTube.

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

1 голос
/ 09 июня 2009

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

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

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

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

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

0 голосов
/ 25 сентября 2010

Так что эта надоедливая ошибка проходит очень длинный путь от iOS 1 до iOS 4.

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

...