Pull to refresh

Landscape ориентация для ViewController в приложении, разработанном под Portrait ориентацию

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

Эта статья - краткое руководство. Ниже вы найдете пример на Objective-C. Да, я написала пример на objc, ведь именно на этом языке мне пришлось решать задачу и как бы мы не хотели жить в мире Swift, правда такова, что большое количество легаси кода делает objc все еще актуальным.

Решение для Swift вы можете найти у Sunny Lee

Для приложения выставлена Portrait ориентация в настройках таргета.
Для приложения выставлена Portrait ориентация в настройках таргета.

1. Определяем поддерживаемую ориентацию в AppDelegate

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

- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
    
    UIViewController *topViewController = [self topViewControllerWithRootViewController:self.window.rootViewController];
    
    if ([topViewController respondsToSelector:@selector(canRotate)]) {
        return UIInterfaceOrientationMaskAllButUpsideDown;
    }
    
    return UIInterfaceOrientationMaskPortrait;
}

Здесь мы проверяем, реализует ли topViewController селектор canRotate. Для того чтобы получить topViewController имплементируем там же метод topViewControllerInRootViewController:

- (UIViewController*)topViewControllerWithRootViewController:(UIViewController *)rootViewController {
    
    if ([rootViewController isKindOfClass:UINavigationController.class]) {
        UINavigationController *navigationController = (UINavigationController *)rootViewController;
        return navigationController.visibleViewController;
    }
  
  	if ([rootViewController isKindOfClass:UITabBarController.class]) {
      	UITabBarController *tabBarController = (UITabBarController *)rootViewController;
      return [self topViewControllerWithRootViewController:tabBarController.selectedViewController];
    }
  
  	if (rootViewController.presentedViewController) {
      return rootViewController.presentedViewController;
    }
    
    return rootViewController;
}

2. Добавляем селектор canRotate во ViewController

Во ViewController, которому мы хотим разрешить менять ориентацию, добавляем метод canRotate.

- (void)canRotate {};

В данном случае метод пустой, т.к. нам достаточно его наличия. Вся логика содержится в AppDelegate. Однако это решение можно расширить, возвращая в методе конкретное значение UIInterfaceOrientationMask и обрабатывать его в AppDelegate, если вы хотите задать разное поведение для разных контроллеров.

3. Возвращаем портретную ориентацию при скрытии ротируемого ViewController

Так как приложение разрабатывается под портретную ориентацию и не предусматривает просмотр основных экранов в лэндскейп, нам нужно вернуть обратно портретную ориентацию при переходе из контроллера в Лэндскейп ориентации. Для этого в методе viewWillDisappear задаем девайсу нужную ориентацию.

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    
    [UIDevice.currentDevice setValue:[NSNumber numberWithInt:UIInterfaceOrientationPortrait] 
     forKey:@"orientation"];
}
Заметка о баге

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

После долгих поисков и изучения похожих вопросов в интернете, я зацепилась за фразу в документации о методе “presentViewController:animated:completion:” - “this method resizes the presented view controller's view based on the presentation style.” А именно его мы использовали. Оказывается, что для landscape режима по умолчанию используется UIModalPresentationFullScreen. А для самого экрана мы задавали  UIModalPresentationCustom. Изменение последнего на FullScreen исправило этот баг.

Источники:

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


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

Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.