Как заставить ориентацию контроллера просмотра в iOS 8?

Перед iOS 8 мы использовали ниже код в сочетании с методами делегирования supportedInterfaceOrientations и shouldAutoRotate, чтобы привязать ориентацию приложения к какой-либо конкретной ориентации. Я использовал ниже фрагмент кода для программного поворота приложения в нужную ориентацию. Во-первых, я меняю ориентацию строки состояния. А затем просто представление и немедленное отклонение модального представления поворачивает представление к желаемой ориентации.

[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeRight animated:YES];
UIViewController *c = [[UIViewController alloc]init];
[self presentViewController:vc animated:NO completion:nil];
[self dismissViewControllerAnimated:NO completion:nil];

Но это не работает в iOS 8. Кроме того, я видел некоторые ответы в переполнении стека, где люди предлагали, чтобы мы всегда избегали этого подхода с iOS 8 и далее.

Чтобы быть более конкретным, мое приложение является универсальным типом приложения. Всего три контроллера.

  • Контроллер First View. Он должен поддерживать все ориентации в iPad и только портрет (кнопка "домой" ) на iPhone.

  • Контроллер второго просмотра. Он должен поддерживать только ландшафт в любых условиях.

  • Третий контроллер просмотра. Он должен поддерживать только ландшафт в любых условиях.

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

Ниже приведены мои методы shouldAutorotate и supportedInterfaceOrientations во втором и третьем контроллерах представления.

-(NSUInteger)supportedInterfaceOrientations{
    return UIInterfaceOrientationMaskLandscapeRight;
}

-(BOOL)shouldAutorotate {
    return NO;
}

Есть ли какое-либо решение для этого или любого лучшего способа блокировки контроллера вида в определенной ориентации для iOS 8. Пожалуйста, помогите!!

Ответ 1

Для iOS 7 - 10:

Objective-C:

[[UIDevice currentDevice] setValue:@(UIInterfaceOrientationLandscapeLeft) forKey:@"orientation"];
[UINavigationController attemptRotationToDeviceOrientation];

Свифт 3:

let value = UIInterfaceOrientation.landscapeLeft.rawValue
UIDevice.current.setValue(value, forKey: "orientation")
UINavigationController.attemptRotationToDeviceOrientation()

Просто позвоните в - viewDidAppear: из представленного контроллера представления.

Ответ 2

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

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

Дайте контроллерам View Power!

Swift 2.3

extension UINavigationController {
    public override func supportedInterfaceOrientations() -> Int {
        return visibleViewController.supportedInterfaceOrientations()
    }
    public override func shouldAutorotate() -> Bool {
        return visibleViewController.shouldAutorotate()
    }
}

extension UITabBarController {
    public override func supportedInterfaceOrientations() -> Int {
        if let selected = selectedViewController {
            return selected.supportedInterfaceOrientations()
        }
        return super.supportedInterfaceOrientations()
    }
    public override func shouldAutorotate() -> Bool {
        if let selected = selectedViewController {
            return selected.shouldAutorotate()
        }
        return super.shouldAutorotate()
    }
}

Swift 3

extension UINavigationController {
    open override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
        return visibleViewController?.supportedInterfaceOrientations ?? super.supportedInterfaceOrientations
    }

    open override var shouldAutorotate: Bool {
        return visibleViewController?.shouldAutorotate ?? super.shouldAutorotate
    }
}

extension UITabBarController {
    open override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
        if let selected = selectedViewController {
            return selected.supportedInterfaceOrientations
        }
        return super.supportedInterfaceOrientations
    }

    open override var shouldAutorotate: Bool {
        if let selected = selectedViewController {
            return selected.shouldAutorotate
        }
        return super.shouldAutorotate
    }
}

Теперь вы можете переопределить метод supportedInterfaceOrientations или вы можете переопределить shouldAutoRotate в контроллере представления, который вы хотите заблокировать, в противном случае вы можете оставить переопределения в других контроллерах представлений, которые вы хотите наследовать по умолчанию для ориентации, указанной в ваш план приложения

Отключить вращение

class ViewController: UIViewController {
    override func shouldAutorotate() -> Bool {
        return false
    }
}

Блокировка на конкретную ориентацию

class ViewController: UIViewController {
    override func supportedInterfaceOrientations() -> Int {
        return Int(UIInterfaceOrientationMask.Landscape.rawValue)
    }
}

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

Блокировка вращения экрана

Ответ 4

Я обнаружил, что если это представленный контроллер представлений, вы можете переопределить preferredInterfaceOrientationForPresentation

Swift:

override func supportedInterfaceOrientations() -> Int {
  return Int(UIInterfaceOrientationMask.Landscape.rawValue)
}

override func preferredInterfaceOrientationForPresentation() -> UIInterfaceOrientation {
  return UIInterfaceOrientation.LandscapeLeft
}

override func shouldAutorotate() -> Bool {
  return false
}

Ответ 5

Этот способ работает для меня в Swift 2 iOS 8.x:

PS (этот метод не требует переопределения функций ориентации, таких как mustautorotate на каждом viewController, только один метод на AppDelegate)

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

Итак, на AppDelegate.swift введите переменную:

var enableAllOrientation = false

Итак, поставьте также эту функцию:

func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> UIInterfaceOrientationMask {
        if (enableAllOrientation == true){
            return UIInterfaceOrientationMask.All
        }
        return UIInterfaceOrientationMask.Portrait
}

Итак, в каждом классе вашего проекта вы можете установить этот var в viewWillAppear:

override func viewWillAppear(animated: Bool)
{
        super.viewWillAppear(animated)
        let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
        appDelegate.enableAllOrientation = true
}

Если вам нужно сделать выбор на основе типа устройства, вы можете сделать это:

override func viewWillAppear(animated: Bool)
    {
        super.viewWillAppear(animated)
        let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
        switch UIDevice.currentDevice().userInterfaceIdiom {
        case .Phone:
        // It an iPhone
           print(" - Only portrait mode to iPhone")
           appDelegate.enableAllOrientation = false
        case .Pad:
        // It an iPad
           print(" - All orientation mode enabled on iPad")
           appDelegate.enableAllOrientation = true
        case .Unspecified:
        // Uh, oh! What could it be?
           appDelegate.enableAllOrientation = false
        }
    }

Ответ 6

Прежде всего - это плохая идея, в общем, что - то неладное с приложением архитектуры, но, sh..t happens, если да, то вы можете попробовать сделать что - то, как показано ниже:

final class OrientationController {

    static private (set) var allowedOrientation:UIInterfaceOrientationMask = [.all]

    // MARK: - Public

    class func lockOrientation(_ orientationIdiom: UIInterfaceOrientationMask) {
        OrientationController.allowedOrientation = [orientationIdiom]
    }

    class func forceLockOrientation(_ orientation: UIInterfaceOrientation) {
        var mask:UIInterfaceOrientationMask = []
        switch orientation {
            case .unknown:
                mask = [.all]
            case .portrait:
                mask = [.portrait]
            case .portraitUpsideDown:
                mask = [.portraitUpsideDown]
            case .landscapeLeft:
                mask = [.landscapeLeft]
            case .landscapeRight:
                mask = [.landscapeRight]
        }

        OrientationController.lockOrientation(mask)

        UIDevice.current.setValue(orientation.rawValue, forKey: "orientation")
    }
}

Чем в AppDelegate

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    // do stuff
    OrientationController.lockOrientation(.portrait)
    return true
}

// MARK: - Orientation

func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
    return OrientationController.allowedOrientation
}

И всякий раз, когда вы хотите изменить ориентацию, делайте так:

OrientationController.forceLockOrientation(.landscapeRight)

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

OrientationController.forceLockOrientation(.portrait)
OrientationController.forceLockOrientation(.landscapeRight)

Все это

Ответ 7

Это должно работать от iOS 6 вверх, но я тестировал его только на iOS 8. Подкласс UINavigationController и переопределяет следующие методы:

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
    return UIInterfaceOrientationLandscapeRight;
}

- (BOOL)shouldAutorotate {
    return NO;
}

Или запросить контроллер видимого вида

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
    return self.visibleViewController.preferredInterfaceOrientationForPresentation;
}

- (BOOL)shouldAutorotate {
    return self.visibleViewController.shouldAutorotate;
}

и реализуйте там методы.

Ответ 8

Это комментарий к комментариям в ответ Sid Shah, касающийся того, как отключить анимацию, используя:

[UIView setAnimationsEnabled:enabled];

код:

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:NO];
    [UIView setAnimationsEnabled:NO];

    // Stackoverflow #26357162 to force orientation
    NSNumber *value = [NSNumber numberWithInt:UIInterfaceOrientationLandscapeRight];
    [[UIDevice currentDevice] setValue:value forKey:@"orientation"];
}

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:NO];
    [UIView setAnimationsEnabled:YES];
}

Ответ 9

Если вы используете navigationViewController, вы должны создать свой собственный суперкласс для этого и переопределить:

- (BOOL)shouldAutorotate {
  id currentViewController = self.topViewController;

  if ([currentViewController isKindOfClass:[SecondViewController class]])
    return NO;

  return YES;
}

это отключит вращение в SecondViewController, но если вы нажмете свой SecondViewController, когда ваше устройство находится на портретной ориентации, тогда ваш SecondViewController появится в портрете Режим.

Предположим, что вы используете раскадровку. Вы должны создать ручной segue (Как) и в методе onClick:

- (IBAction)onPlayButtonClicked:(UIBarButtonItem *)sender {
  NSNumber *value = [NSNumber numberWithInt:UIInterfaceOrientationLandscapeLeft];
  [[UIDevice currentDevice] setValue:value forKey:@"orientation"];
  [self performSegueWithIdentifier:@"PushPlayerViewController" sender:self];
}

Это заставит пейзажную ориентацию перед отключением функции авторотации вашего суперкласса.

Ответ 10

В Xcode 8 методы преобразуются в свойства, поэтому следующее работает с Swift:

override public var supportedInterfaceOrientations: UIInterfaceOrientationMask {
    return UIInterfaceOrientationMask.portrait
}

override public var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {
    return UIInterfaceOrientation.portrait
}

override public var shouldAutorotate: Bool {
    return true
}

Ответ 11

Я пробовал много решений, но тот, который работал, следующий:

Нет необходимости редактировать info.plist в ios 8 и 9.

- (BOOL) shouldAutorotate {
    return NO;
}   

- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
    return (UIInterfaceOrientationPortrait | UIInterfaceOrientationPortraitUpsideDown);
}

Возможные ориентации из документации Apple:

UIInterfaceOrientationUnknown

Ориентация устройства не может быть определена.

UIInterfaceOrientationPortrait

Устройство находится в портретном режиме, при этом устройство удерживается в вертикальном положении, а Кнопка "Дом" внизу.

UIInterfaceOrientationPortraitUpsideDown

Устройство находится в портретном режиме, но вверх ногами, при этом устройство удерживается вертикально и кнопка "вверх" вверху.

UIInterfaceOrientationLandscapeLeft

Устройство находится в ландшафтном режиме, при этом устройство удерживается в вертикальном положении, а на левой стороне.

UIInterfaceOrientationLandscapeRight

Устройство находится в ландшафтном режиме, при этом устройство удерживается в вертикальном положении, а домашняя кнопка с правой стороны.

Ответ 12

Сочетание ответов Sids и Koreys работало для меня.

Расширение контроллера навигации:

extension UINavigationController {
    public override func shouldAutorotate() -> Bool {
        return visibleViewController.shouldAutorotate()
    }
}

Затем отключить вращение на одиночном представлении

class ViewController: UIViewController {
    override func shouldAutorotate() -> Bool {
        return false
    }
}

И вращаясь до соответствующей ориентации перед segue

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    if (segue.identifier == "SomeSegue")
    {
        let value = UIInterfaceOrientation.Portrait.rawValue;
        UIDevice.currentDevice().setValue(value, forKey: "orientation")
    }
}

Ответ 13

Верхнее решение выше:

let value = UIInterfaceOrientation.LandscapeLeft.rawValue
UIDevice.currentDevice().setValue(value, forKey: "orientation")

didnt'work для меня, когда я назвал его в viewDidAppear представленного контроллера представления. Однако он работал, когда я вызывал его в prepForSegue в представлении контроллера представления.

(Извините, недостаточно очков репутации, чтобы прокомментировать это решение, поэтому мне пришлось добавить его так)

Ответ 14

Мои требования

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

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

Сначала определите supportedInterfaceOrientationsForWindow в AppDelegate.swift

var portrait = true
func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> UIInterfaceOrientationMask {
    if portrait {
        return .Portrait
    } else {
        return .Landscape
    }
}

Во-вторых, в вашем основном контроллере представления определите следующие функции

override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
    print("\(#function)")
    return .Portrait
}

override func preferredInterfaceOrientationForPresentation() -> UIInterfaceOrientation {
    return .Portrait
}

override func shouldAutorotate() -> Bool {
    return false
}

Затем вам нужно подклассом AVPlayerViewController

class MyPlayerViewController: AVPlayerViewController {

    var size: CGSize?

    var supportedOrientationMask: UIInterfaceOrientationMask?
    var preferredOrientation: UIInterfaceOrientation?

    override func viewDidLoad() {
        super.viewDidLoad()

        if let size = size {
            if size.width > size.height {
                self.supportedOrientationMask =[.LandscapeLeft,.LandscapeRight]
                self.preferredOrientation =.LandscapeRight
            } else {
                self.supportedOrientationMask =.Portrait
                self.preferredOrientation =.Portrait
            }
        }
    }

Переопределите эти три функции в MyPlayerViewController.swift

override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
    return self.supportedOrientationMask!
}

override func preferredInterfaceOrientationForPresentation() -> UIInterfaceOrientation {
    return self.preferredOrientation!
}

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

override func shouldAutorotate() -> Bool {
    return true
}

Наконец, создайте экземпляр MyPlayerViewController в вашем контроллере представления и задайте значение размера свойства.

let playerViewController = MyPlayerViewController()

// Get the thumbnail  
let thumbnail = MyAlbumFileManager.sharedManager().getThumbnailFromMyVideoMedia(......)

let size = thumbnail?.size
playerViewController.size = size

Инициируйте плеер с помощью videoUrl, а затем назначьте его игроку playerViewController. Счастливое кодирование!

Ответ 15

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    [UIViewController attemptRotationToDeviceOrientation];
}

Ответ 16

[iOS9 +] Если бы кто-то перетащил сюда весь путь, так как ни одно из вышеперечисленных решений не работало, и если вы представляете представление, которое хотите изменить ориентацию по segue, вы можете проверить это.

Проверьте тип презентации segue. Мой был "в текущем контексте". Когда я изменил его на полноэкранный режим, он сработал.

Благодаря @GabLeRoux я нашел это решение.

  • Это изменение работает только в сочетании с решениями выше.

Ответ 17

В соответствии с Ответ Кори Хинтон

Swift 2.2:

extension UINavigationController {
    public override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
        return visibleViewController!.supportedInterfaceOrientations()
    }
    public override func shouldAutorotate() -> Bool {
        return visibleViewController!.shouldAutorotate()
    }
}


extension UITabBarController {
    public override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
        if let selected = selectedViewController {
            return selected.supportedInterfaceOrientations()
        }
        return super.supportedInterfaceOrientations()
    }
    public override func shouldAutorotate() -> Bool {
        if let selected = selectedViewController {
            return selected.shouldAutorotate()
        }
        return super.shouldAutorotate()
    }
}

Отключить вращение

override func shouldAutorotate() -> Bool {
    return false
}

Блокировка для конкретной ориентации

override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
    return UIInterfaceOrientationMask.Portrait
}

Ответ 18

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

Я создал следующий objective-c проект github.com/GabLeRoux/RotationLockInTabbedViewChild с рабочим примером TabbedViewController где разрешено одно дочернее представление, а другое дочернее представление заблокировано в портрете.

Это не идеально, но он работает, и одна и та же идея должна работать для других видов корневых представлений, таких как NavigationViewController.:)

Child view locks parent orientation

Ответ 19

У меня такая же проблема, и у меня так много времени. Итак, теперь у меня есть мое решение. Моя настройка приложения - это только портрет поддержки. Однако некоторые экраны в моем приложении должны иметь только ландшафт. Я исправляю его, имея переменную isShouldRotate в AppDelegate. И функция AppDelegate:

func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> Int {
    if isShouldRotate == true {
        return Int(UIInterfaceOrientationMask.Landscape.rawValue)
    }
    return Int(UIInterfaceOrientationMask.Portrait.rawValue)
}

И наконец, когда ViewControllerA требуется пейзаж. Просто сделайте это: перед push/present до ViewControllerA присвойте isShouldRotate значение true. Не забывайте, когда pop/увольняют этот контроллер, присвойте isShouldRotate false в viewWillDisappear.

Ответ 20

Согласно решению, представленному @sid-sha, вы должны поместить все в метод viewDidAppear:, иначе вы не получите didRotateFromInterfaceOrientation:, поэтому что-то вроде:

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    UIInterfaceOrientation interfaceOrientation = [[UIApplication sharedApplication] statusBarOrientation];
    if (interfaceOrientation == UIInterfaceOrientationLandscapeLeft ||
        interfaceOrientation == UIInterfaceOrientationLandscapeRight) {
        NSNumber *value = [NSNumber numberWithInt:interfaceOrientation];
        [[UIDevice currentDevice] setValue:value forKey:@"orientation"];
    }
}

Ответ 21

Мое решение

В AppDelegate:

func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> UIInterfaceOrientationMask {
    if let topController = UIViewController.topMostViewController() {
        if topController is XXViewController {
            return [.Portrait, .LandscapeLeft]
        }
    }
    return [.Portrait]
}

XXViewController - это ViewController, для которого вы хотите поддерживать ландшафтный режим.

Затем Sunny Shah solution будет работать в вашей XXViewController на любой версии iOS:

let value = UIInterfaceOrientation.LandscapeLeft.rawValue
UIDevice.currentDevice().setValue(value, forKey: "orientation")

Это функция утилиты, чтобы найти самый верхний вид ViewController.

extension UIViewController {

    /// Returns the current application top most view controller.
    public class func topMostViewController() -> UIViewController? {
        let rootViewController = UIApplication.sharedApplication().windows.first?.rootViewController
        return self.topMostViewControllerOfViewController(rootViewController)
    }



    /// Returns the top most view controller from given view controller stack.
    class func topMostViewControllerOfViewController(viewController: UIViewController?) -> UIViewController? {
        // UITabBarController
        if let tabBarController = viewController as? UITabBarController,
           let selectedViewController = tabBarController.selectedViewController {
            return self.topMostViewControllerOfViewController(selectedViewController)
        }

        // UINavigationController
        if let navigationController = viewController as? UINavigationController,
           let visibleViewController = navigationController.visibleViewController {
            return self.topMostViewControllerOfViewController(visibleViewController)
        }

        // presented view controller
        if let presentedViewController = viewController?.presentedViewController {
            return self.topMostViewControllerOfViewController(presentedViewController)
        }

        // child view controller
        for subview in viewController?.view?.subviews ?? [] {
            if let childViewController = subview.nextResponder() as? UIViewController {
                return self.topMostViewControllerOfViewController(childViewController)
            }
        }

        return viewController
    }

}

Ответ 22

Используйте это для блокировки ориентации контроллера просмотра, проверенной на IOS 9:

//Заблокировать ориентацию в альбомной ориентации

-(UIInterfaceOrientationMask)supportedInterfaceOrientations {
    return UIInterfaceOrientationMaskLandscapeRight;
}

-(NSUInteger)navigationControllerSupportedInterfaceOrientations:(UINavigationController *)navigationController {
    return UIInterfaceOrientationMaskLandscapeRight;
}

Ответ 23

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

- (void) viewWillAppear:(BOOL)animated
{
    [UIViewController attemptRotationToDeviceOrientation];
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
    return (toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft);
}

-(BOOL)shouldAutorotate
{
    return NO;
}

- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskLandscapeLeft;
}

В этом примере вам также нужно будет настроить разрешенные ориентации устройства на "Пейзаж слева" в настройках вашего проекта (или непосредственно в info.plist). Просто измените конкретную ориентацию, которую хотите усилить, если вы хотите что-то другое, кроме LandscapeLeft.

Ключом для меня был вызов attemptRotationToDeviceOrientation в viewWillAppear - без этого вид не будет правильно вращаться без физического поворота устройства.

Ответ 24

Для меня VC верхнего уровня необходим для реализации переопределения ориентации. Использование VC вниз по стеку не будет иметь никакого эффекта, если верхний VC не реализует.

VC-main
    |
    -> VC 2
        |
        -> VC 3

Только VC-Main прослушивается, по сути, в моем тестировании.

Ответ 25

Похоже, что даже ты здесь так много ответов, никого не было достаточно для меня. Я хотел принудительно установить ориентацию, а затем при возврате вернуться к ориентации устройства, но [UIViewController attemptRotationToDeviceOrientation]; просто не сработало. Что также усложнило, так это то, что я добавил shouldAutorotate в false, основываясь на каком-то ответе, и не смог получить желаемых эффектов для правильного поворота назад во всех сценариях.

Вот что я сделал:

Перед нажатием контроллера в вызове в его конструкторе инициализации это:

_userOrientation = UIDevice.currentDevice.orientation;
[UIDevice.currentDevice setValue:@(UIInterfaceOrientationPortrait) forKey:@"orientation"];
[self addNotificationCenterObserver:@selector(rotated:)
                               name:UIDeviceOrientationDidChangeNotification];

Поэтому я сохраняю последнюю ориентацию устройства и регистрируюсь на событие изменения ориентации. Событие смены ориентации простое:

- (void)rotated:(NSNotification*)notification {
    _userOrientation = UIDevice.currentDevice.orientation;
}

И при исчезновении вида я просто возвращаюсь к любой ориентации, которую я имею как userOreintation:

- (void)onViewDismissing {
    super.onViewDismissing;
    [UIDevice.currentDevice setValue:@(_userOrientation) forKey:@"orientation"];
    [UIViewController attemptRotationToDeviceOrientation];
}

И это должно быть там тоже:

- (BOOL)shouldAutorotate {
    return true;
}

- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
    return UIInterfaceOrientationMaskPortrait;
}

А также навигационный контроллер должен делегировать shouldAutorotate и supportInterfaceOrientations, но, как я полагаю, большинство людей уже имеют.

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