Force device rotation with +attemptRotationToDeviceOrientation failing
Asked Answered
S

2

9

According to the UIViewController docs here,

https://developer.apple.com/library/ios/documentation/uikit/reference/UIViewController_Class/Reference/Reference.html#//apple_ref/occ/clm/UIViewController/attemptRotationToDeviceOrientation

using [UIViewController attemptRotationToDeviceOrientation] forces the system to attempt to rotate the interface to a new orientation. I am attempting to force an interface rotation from portrait to landscape by:

  • setting a forceLandscape flag before attempting the rotation
  • Calling [UIViewController attemptRotationToDeviceOrientation]
  • implementing -supportedInterfaceOrientations in my view controller and checking a flag to change the supported orientations, e.g.

This forces -supportedInterfaceOrientations to be called again, which looks like

- (NSUInteger)supportedInterfaceOrientations {
    return self.forceLandscape ? UIInterfaceOrientationMaskLandscapeLeft : UIInterfaceOrientationMaskPortrait;
}

Even though -supportedInterfaceOrientations is being called and returns the new orientation, the interface does not rotate. I have confirmed that the project allows all orientations and that this view controller is the window's root view controller. Has anyone else run into this problem and found a solution? I am aware of all the hacks to fix this (presenting and dismissing modals, etc), but I would like a clean solution if possible. I have verified this issue on iOS 6 and 7.

Below is the complete code to reproduce:

@interface ALTViewController ()
@property (nonatomic, assign) BOOL forceLandscape;
@end

@implementation ALTViewController

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {

        self.forceLandscape = NO;
    }
    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];

    UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
    [button setTitle:@"button" forState:UIControlStateNormal];
    [button sizeToFit];
    [self.view addSubview:button];
    button.center = self.view.center;
    [button addTarget:self
               action:@selector(buttonPressed:)
     forControlEvents:UIControlEventTouchUpInside];
}

- (void)buttonPressed:(id)sender
{
    self.forceLandscape = YES;
    [UIViewController attemptRotationToDeviceOrientation];
}

- (NSUInteger)supportedInterfaceOrientations
{
    return self.forceLandscape ? UIInterfaceOrientationMaskLandscapeLeft : UIInterfaceOrientationMaskPortrait;
}
Sun answered 1/10, 2013 at 20:25 Comment(2)
Within your question, you seem to mix the term device with the term interface which makes it a bit hard to understand what you are attempting to achieve. Are you attempting to rotate the interface to new orientation even though the device remains in the same orientation?Counterpart
Till - agreed :) I just edited the question for clarity. You are right, I am trying to force the interface from portrait to landscape orientation while the device remains in portrait orientation.Sun
P
12

A call to attemptRotationToDeviceOrientation only rotates the orientation of the interface, if and only if, the device itself has been rotated.

If your view controller only supported portrait orientation, for example, and the user rotated the device to landscape orientation, no interface rotation would occur. While the device was in landscape orientation, let's say your code toggles a flag which allows for landscape orientation. What would happen to the interface orientation? Nothing, because device orientation has already occurred. To get the interface orientation to match the device orientation, you would need to call attemptRotationToDeviceOrientation.

In my own code, I recently created a custom, multi-part view animation that didn't look right if interface rotation occurred in the middle of it. So I turned off interface rotation during the brief animation. If the user rotated the device during the animation, no interface orientation would occur. However, when interface orientation was turned back on, the interface would not rotate to match the device orientation until I called attemptRotationToDeviceOrientation.

Pricecutting answered 1/10, 2013 at 20:39 Comment(4)
I am beginning to agree with you. However, the docs suggest that we can change the interfaces that are supported based on app specific conditions: "Some view controllers may want to use app-specific conditions to determine what interface orientations are supported. If your view controller does this, when those conditions change, your app should call this class method. The system immediately attempts to rotate to the new orientation."Sun
I asked a similar question here #20987749 and I guess my question is how is attemptRotationToDeviceOrientation useful if it doesn't actually rotate? This used to be possible pre-iOS6 with [UIDevice currentDevice] setOrientation. This is a hack that works in iOS7 with a complier warnind, but I'll be won't be accepted for App Store submission: objc_msgSend([UIDevice currentDevice], @selector(setOrientation:), UIInterfaceOrientationPortrait );Angleworm
` Came here to say that this call is failing sometimes on the Simulator as of XCode 8.3, but when i try on the device it does work.Aulic
@Pricecutting what do you mean by "So I turned off interface rotation during the brief animation"? How do you do that?Selfabsorption
A
2

You could try to achieve something with: setStatusBarOrientation:animated: https://developer.apple.com/library/ios/documentation/uikit/reference/UIApplication_Class/Reference/Reference.html#//apple_ref/occ/instm/UIApplication/setStatusBarOrientation:animated:

this fixed for me a similar issue.

Abixah answered 2/10, 2013 at 8:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.