How do I programmatically set device orientation in iOS 7?

I am working on an iPad app, using AutoLayout, where if the user enables a certain mode ("heads-up" mode), I want to support only portrait (or portrait upside down) orientation, and furthermore, if the device is in landscape, I'd like to automatically switch to portrait mode.

In the top view controller, I have the following:

- (NSUInteger) supportedInterfaceOrientations {
    if (self.modeHeadsUp) {
        return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown;
    } else {
        return UIInterfaceOrientationMaskAll;
    }
}

- (BOOL) shouldAutorotate {
    return TRUE;
}

Based on answers I've seen elsewhere here, the answer seems to be that I should use "application setStatusBarOrientation". Therefore, in the method where the user has selected "heads-up" mode, I have included:

    UIApplication *application = [UIApplication sharedApplication];
    [application setStatusBarOrientation:UIInterfaceOrientationPortrait
                                animated:YES];

However, this simply doesn't seem to do anything. While I can physically move the device to get it to rotate into portrait, it doesn't do so automatically.

In fact, when in landscape mode after running the above code to attempt to programmatically set the orientation, when I query the application "statusBarOrientation" with the following code, it remains at "4" for landscape:

UIApplication *application = [UIApplication sharedApplication];
int orientation = [application statusBarOrientation];
self.movesTextView.text = [NSString stringWithFormat:@"ORIENTATION %d", orientation];

It seemed like maybe autolayout wasn't being triggered with the setStatusBarOrientation, so I attempted to add this code after, to no effect:

    [super updateViewConstraints];
    [self.view updateConstraints];

I realize Apple wants to leave device orientation in the hands of the user. However, I'd like to be able to support landscape mode when not in "heads-up" mode.

Am I missing something to be able to force orientation change?

Answers


For iOS 7 & 8:

Objective-C:

NSNumber *value = [NSNumber numberWithInt:UIInterfaceOrientationLandscapeLeft];
[[UIDevice currentDevice] setValue:value forKey:@"orientation"];

Swift 3+:

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

I call it in - viewDidAppear:.


Use this. Perfect solution to orientation problem..ios7 and earlier

[[UIDevice currentDevice] setValue:
    [NSNumber numberWithInteger: UIInterfaceOrientationPortrait]
        forKey:@"orientation"];

You need to call attemptRotationToDeviceOrientation (UIViewController) to make the system call your supportedInterfaceOrientations when the condition has changed.


This works for me on Xcode 6 & 5.

- (BOOL)shouldAutorotate {
    return YES;
}
- (NSUInteger)supportedInterfaceOrientations {
    return (UIInterfaceOrientationMaskPortrait);
}

NSNumber *value = [NSNumber numberWithInt:UIInterfaceOrientationLandscapeLeft]; [[UIDevice currentDevice] setValue:value forKey:@"orientation"];

does work but you have to return shouldAutorotate with YES in your view controller

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

But if you do that, your VC will autorotate if the user rotates the device... so I changed it to:

@property (nonatomic, assign) BOOL shouldAutoRotate;

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

and I call

- (void)swithInterfaceOrientation:(UIInterfaceOrientation)orientation
{
    self.rootVC.shouldAutoRotate = YES;

    NSNumber *value = [NSNumber numberWithInt: orientation];
    [[UIDevice currentDevice] setValue:value forKey:@"orientation"];
}

to force a new orientation with a button-click. To set back shouldAutoRotate to NO, I added to my rootVC

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
    self.shouldAutoRotate = NO;
}

PS: This workaround does work in all simulators too.


The only way that worked for me is presenting dummy modal view controller.

UIViewController* dummyVC = [[UIViewController alloc] init];
dummyVC.view = [[UIView alloc] init];
[self presentModalViewController:dummyVC animated:NO];
[self dismissModalViewControllerAnimated:NO];

Your VC will be asked for updated interface orientations when modal view controller is dismissed.

Curious thing is that UINavigationController does exactly this when pushing/popping child view controllers with different supported interface orientations (tested on iOS 6.1, 7.0).


This solution lets you force a certain interface orientation, by temporarily overriding the value of UIDevice.current.orientation and then asking the system to rotate the interface to match the device's rotation:

Important: This is a hack, and could stop working at any moment

Add the following in your app's root view controller:

class RootViewController : UIViewController {
    private var _interfaceOrientation: UIInterfaceOrientation = .portrait
    override var supportedInterfaceOrientations: UIInterfaceOrientationMask { return UIInterfaceOrientationMask(from: _interfaceOrientation) }
    override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation { return _interfaceOrientation }

    override func viewDidLoad() {
        super.viewDidLoad()
        // Register for notifications
        NotificationCenter.default.addObserver(self, selector: #selector(RootViewController.handleInterfaceOrientationChangeRequestedNotification(_:)), name: .interfaceOrientationChangeRequested, object: nil)
    }

    deinit { NotificationCenter.default.removeObserver(self) }

    func handleInterfaceOrientationChangeRequestedNotification(_ notification: Notification) {
        guard let interfaceOrientation = notification.object as? UIInterfaceOrientation else { return }
        _interfaceOrientation = interfaceOrientation
        // Set device orientation
        // Important:
        // • Passing a UIDeviceOrientation here doesn't work, but passing a UIInterfaceOrientation does
        // • This is a hack, and could stop working at any moment
        UIDevice.current.setValue(interfaceOrientation.rawValue, forKey: "orientation")
        // Rotate the interface to the device orientation we just set
        UIViewController.attemptRotationToDeviceOrientation()
    }
}

private extension UIInterfaceOrientationMask {

    init(from interfaceOrientation: UIInterfaceOrientation) {
        switch interfaceOrientation {
        case .portrait: self = .portrait
        case .landscapeLeft: self = .landscapeLeft
        case .landscapeRight: self = .landscapeRight
        case .portraitUpsideDown: self = .portraitUpsideDown
        case .unknown: self = .portrait
        }
    }
}

extension Notification.Name {
    static let interfaceOrientationChangeRequested = Notification.Name(rawValue: "interfaceOrientationChangeRequested")
}

Make sure all interface orientations are checked under "Deployment Info":

Request interface orientation changes where you need them:

NotificationCenter.default.post(name: .interfaceOrientationChangeRequested, object: UIInterfaceOrientation.landscapeRight)

If you want to lock the main view of your app to portrait, but want to open popup views in landscape, and you are using tabBarController as rootViewController as I am, you can use this code on your AppDelegate.

AppDelegate.h

@interface AppDelegate : UIResponder <UIApplicationDelegate, UITabBarControllerDelegate>

@property (strong, nonatomic) UIWindow *window;
@property (strong, nonatomic) UITabBarController *tabBarController;

@end

AppDelegate.m

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

    // Create a tab bar and set it as root view for the application
    self.tabBarController = [[UITabBarController alloc] init];
    self.tabBarController.delegate = self;
    self.window.rootViewController = self.tabBarController;

    ...
}

- (NSUInteger)tabBarControllerSupportedInterfaceOrientations:(UITabBarController *)tabBarController
{
    return UIInterfaceOrientationMaskPortrait;
}

- (UIInterfaceOrientation)tabBarControllerPreferredInterfaceOrientationForPresentation:(UITabBarController *)tabBarController
{
    return UIInterfaceOrientationPortrait;
}

It works very well.

In your viewController you want to be presented in landscape, you simply use the following:

- (NSUInteger)supportedInterfaceOrientations {
    return UIInterfaceOrientationMaskLandscape;
}

- (BOOL)shouldAutorotate {
    return YES;
}

  1. Add this statement into AppDelegate.h

    //whether to allow cross screen marker 
    @property (nonatomic, assign) allowRotation BOOL;
    
  2. Write down this section of code into AppDelegate.m

    - (UIInterfaceOrientationMask) application: (UIApplication *) supportedInterfaceOrientationsForWindow: application (UIWindow *) window {
        If (self.allowRotation) {
            UIInterfaceOrientationMaskAll return;
        }
        UIInterfaceOrientationMaskPortrait return;
    }
    
  3. Change the allowRotation property of delegate app


If you have a UIViewController that should stay in Portrait mode, simply add this override and you're all set.

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

The best part is there is no animation when this view is shown, it's just already in the correct orientation.


The base UINavigationController should have the below callback so that the child items can decide what orientation they want.

-(NSUInteger)supportedInterfaceOrientations {
    UIViewController *topVC = self.topViewController;
    return topVC.supportedInterfaceOrientations;
}

-(BOOL)shouldAutorotate {
   UIViewController *topVC = self.topViewController;
   return [topVC shouldAutorotate];
}

If you want only portrait mode, in iOS 9 (Xcode 7) you can:

  • Going to Info.plist
  • Select "Supported interface orientations" item
  • Delete "Landscape(left home button)" and "Landscape(right home button)"


I was in a similar problem than you. I need to lock device orientation for some screens (like Login) and allow rotation in others.

After a few changes and following some answers below I did it by:

  • Enabling all the orientations in the Project's Info.plist.

  • Disabling orientation in those ViewControllers where I need the device not to rotate, like in the Login screen in my case. I needed to override shouldAutorotate method in this VC:

-(BOOL)shouldAutorotate{ return NO; }

Hope this will work for you.


here it is a FULL WORKING example for iOS 7, 8, 9, 10 how to change app orientation to its current opposite

Objective-C

- (void)flipOrientation
{
    NSNumber *value;
    UIInterfaceOrientation currentOrientation = [[UIApplication sharedApplication] statusBarOrientation];
    if(UIInterfaceOrientationIsPortrait(currentOrientation))
    {
        if(currentOrientation == UIInterfaceOrientationPortrait)
        {
            value = [NSNumber numberWithInt:UIInterfaceOrientationPortraitUpsideDown];
        }
        else //if(currentOrientation == UIInterfaceOrientationPortraitUpsideDown)
        {
            value = [NSNumber numberWithInt:UIInterfaceOrientationPortrait];
        }
    }
    else
    {
        if(currentOrientation == UIInterfaceOrientationLandscapeRight)
        {
            value = [NSNumber numberWithInt:UIInterfaceOrientationLandscapeLeft];
        }
        else //if(currentOrientation == UIInterfaceOrientationLandscapeLeft)
        {
            value = [NSNumber numberWithInt:UIInterfaceOrientationLandscapeRight];
        }
    }
    [[UIDevice currentDevice] setValue:value forKey:@"orientation"];
    [UIViewController attemptRotationToDeviceOrientation];
}

Swift 3

func flipOrientation() -> Void
{
    let currentOrientation : UIInterfaceOrientation = UIApplication.shared.statusBarOrientation
    var value : Int = 0;
    if(UIInterfaceOrientationIsPortrait(currentOrientation))
    {
        if(currentOrientation == UIInterfaceOrientation.portrait)
        {
            value = UIInterfaceOrientation.portraitUpsideDown.rawValue
        }
        else //if(currentOrientation == UIInterfaceOrientation.portraitUpsideDown)
        {
            value = UIInterfaceOrientation.portrait.rawValue
        }
    }
    else
    {
        if(currentOrientation == UIInterfaceOrientation.landscapeRight)
        {
            value = UIInterfaceOrientation.landscapeLeft.rawValue
        }
        else //if(currentOrientation == UIInterfaceOrientation.landscapeLeft)
        {
            value = UIInterfaceOrientation.landscapeRight.rawValue
        }
    }
    UIDevice.current.setValue(value, forKey: "orientation")
    UIViewController.attemptRotationToDeviceOrientation()
}

Try this along with your code.

-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation  

-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration

once user select any option then call this method because user can be in landscape mode and then he can set only portrait mode in same view controller so automatically view should be moved to portrait mode so in that button acton call this

-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration

This worked me perfectly....

NSNumber *value = [NSNumber numberWithInt:UIDeviceOrientationPortrait];
[[UIDevice currentDevice] setValue:value forKey:@"orientation"];

For those like me who struggled to get @Sunny Shah accepted answer to work on iPads. You need to set the "Requires full screen" checkbox on in the project settings. Note that this will prevent your app from working on multitasking mode which may or not be acceptable.


Need Your Help

Is there a way to use Webkit Inspector Remote Debugging in iPad?

javascript android ipad debugging mobile

I have an iPad and I am wondering if I can remote debug it from the desktop using Webkit Inspector? As I understand it, it requires you to launch the browser with a command line switch. I do not th...

Android heterogeneous gridview like pinterest?

java android pinterest

Is it possible to create pinterest like layout on Android using GridView? I want to create image gallery using GridView but I am not sure if it is good solution. I do not want create three LinearLa...