如何在 iOS7中以编程方式设置设备方向?

我正在开发一个 iPad 应用程序,使用 AutoLayout,如果用户启用某种模式(“抬头”模式) ,我希望只支持纵向(或纵向倒置)方向,此外,如果设备是横向的,我希望自动切换到纵向模式。

在 top view 控制器中,我有以下内容:

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


- (BOOL) shouldAutorotate {
return TRUE;
}

根据我在这里其他地方看到的答案,答案是 看起来,我应该使用“ application setStatusBarOriation”。因此,在用户选择“提醒”模式的方法中,我包括:

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

然而,这似乎没有任何作用。虽然我可以物理地移动设备使其旋转成肖像,但它不会自动这样做。

事实上,当在横向模式下运行以上代码尝试通过编程方式设置方向时,当我使用以下代码查询应用程序“ statusBarOrient”时,横向模式仍然是“4”:

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

似乎自动布局并没有被 setStatusBarOrient 触发,所以我尝试在后面添加这段代码,但是没有效果:

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

我意识到苹果想把设备定位留给用户。然而,我希望能够支持横向模式时,不在“提醒”模式。

我是不是错过了什么能够强制改变方向的东西?

112659 次浏览

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:.

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

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

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;
}

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).

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

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

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 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)"

enter image description here

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.

enter image description here

  • 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.

  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.

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":

Interface Orientations

Request interface orientation changes where you need them:

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

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()
}

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.

enter image description here

2020 Swift 5 :

override var supportedInterfaceOrientations:UIInterfaceOrientationMask {
return .portrait
}