supportedInterfaceOrientations not working

The Apple designed way to do this for UINavigationController is via UINavigationControllerDelegate.

Usually I just update this delegate to do the following and delegate it to the top displaying controller in the navigation controller's stack:

#pragma mark - UINavigationControllerDelegate

- (NSUInteger)navigationControllerSupportedInterfaceOrientations:(UINavigationController *)navigationController
{
    return [navigationController.topViewController supportedInterfaceOrientations];
}

In my opinion the UINavigationController should just default to the above behavior but this is the api Apple has provided :)

As for the info plist file I uncheck all of the options and handle it all in code as it has caused issues for me in the past and I got tired of dealing with it.


Just for you all information, create a new project single view based Application, implement the should auto rotate delegate and the supportedInterface orientation in viewController add breakpoint in it.

The run (universal mode)

  1. on iphone simulator : execution stops at breakpoints
  2. on ipad Simulator : executions DOESN'T STOP (delegate methods are never called !!!!)
  3. on ipad device : ok

So don't waste too mush time on it like I juste Did, XCode 7.3 is BUGGED


Here is how I do it.
Make a UINavigationController parent class.
inside you UINavigationController (parent) override these methods like:

- (NSUInteger)supportedInterfaceOrientations
{
    if([self.topViewController respondsToSelector:@selector(supportedInterfaceOrientationsForThisContorller)])
    {
        return(NSInteger)[self.topViewController performSelector:@selector(supportedInterfaceOrientationsForThisContorller) withObject:nil];
    }
    return UIInterfaceOrientationPortrait;
}

- (BOOL)shouldAutorotate
{
    if([self.visibleViewController respondsToSelector:@selector(shouldAutorotateNow)])
    {
        BOOL autoRotate = (BOOL)[self.visibleViewController
                            performSelector:@selector(shouldAutorotateNow)
                            withObject:nil];
        return autoRotate;

    }
    return NO;
}

Now your NavigationController should be a subclass of the UINavigationContorller parent

Swift 3:

Inside your UINavigationController subclass do this

override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
        get {
            return self.topViewController?.supportedInterfaceOrientations ?? .all
        }
    }

override var shouldAutorotate: Bool {
        return self.topViewController?.shouldAutorotate ?? false
    }

Update Taken from Matt's answer if you don't want to subclass:

first: make your viewController a delegate of the navigationController in viewDidLoad

self.navigationController?.delegate = self

Then declare UIViewController extension to respond to the delegate method as shown below:

extension UIViewController: UINavigationControllerDelegate {
    public func navigationControllerSupportedInterfaceOrientations(_ navigationController: UINavigationController) -> UIInterfaceOrientationMask {
        return navigationController.topViewController?.supportedInterfaceOrientations
    }
}

For simplicity, for iPad, if Supported interface orientations (iPad) property in info.plist includes all the four orientations, with UIRequiresFullScreen property value as NO, iOS will treat your app as supporting split view. If an app supports split view feature, you can not disable it from rotating, at least by the ways above.


Let's go deep into details.

First, we need to know which factors will influence the screen orientation:

1. Project settings(info.plist)

There are two places we can do this setting:

  • Project->Target->General->Device Orientation

    screen orientation setting in project setting

  • info.plist of the project

    screen orientation setting in info.plist

The two methods are the same and one will automatically sync the other setting. In info.plist file, Supported interface orientations (iPad) property is for iPad setting (choose iPad for Device section), and Supported interface orientations property is for iPhone and Univeral (choose iPhone or Universal for Device section, and these two settings will always be the same.).

2. Set supported screen orientations programmatically

There are two ways to configure the supported orientations while coding:

  • supportedInterfaceOrientationsForWindow: method in UIApplicationDelegate
  • supportedInterfaceOrientations method in UIViewController

The iOS system will check the common orientations defined in the two methods and use the common orientations as the supported orientations for the view controller.

3. Set shouldAutorotate property in UIViewController

This is a read-only property and used for control if the view controller is rotatable or not. You can override the property in your view controller, or even change the property to read-write property, so that you can modify the property while your app is running. For example:

override public var shouldAutorotate: Bool {
    get {
        return self.shouldAutorotateVariable
    }
    set {
        self.shouldAutorotateVariable = newValue
    }
}

shouldAutorotateVariable is a private property in your view controller.

iPhone

If a view controller's shouldAutorotate property is set to false, iOS will omit the following settings:

  • supportedInterfaceOrientationsForWindow: method in UIApplicationDelegate
  • supportedInterfaceOrientations method in UIViewController

iOS will only use Project settings (info.plist).

If the shouldAutorotate property is not overriden or is set to true, then iOS will use the common orientations defined in the following two methods:

  • supportedInterfaceOrientationsForWindow: method in UIApplicationDelegate
  • supportedInterfaceOrientations method in UIViewController

If the above two methods are not supplied, iOS will use the Project settings (info.plist).

iPad

From iOS9, iPad supports split view feature. Refer to Slide Over and Split View Quick Start for the setting of split view.

For simplicity, if Supported interface orientations (iPad) property in info.plist includes all the four orientations, with UIRequiresFullScreen property value as NO, iOS will treat your app as supporting split view. If the app supports split view, the following two settings will be omitted:

  • supportedInterfaceOrientationsForWindow: method in UIApplicationDelegate
  • supportedInterfaceOrientations method in UIViewController

The shouldAutorotate property in UIViewController will also be omitted.

That is to say, if an app supports split view feature, you can not disable it from rotating, at least by the ways above.

If Supported interface orientations (iPad) property in info.plist value doesn't include all four orientations, or UIRequiresFullScreen property is set to YES, the app will not support split view, and the screen orientation control logic is the same with iPhone above.

Note: for the Project settings (info.plist), it's better to set the following three properties directly inside info.plist:

  • Supported interface orientations (iPad)
  • Supported interface orientations
  • UIRequiresFullScreen

It is confusing if you try to set through Project->Target->General->Device Orientation: even Device is set as Universe, the setting in iPad will still take effect.