iOS 6: How do I restrict some views to portrait an

2019-01-02 16:50发布

I have an iPhone app that uses a UINavigationController to present a drill-down interface: First one view, then another, up to four levels deep. I want the first three views restricted to portrait orientation and only the last view should be allowed to rotate to landscape. When returning from the fourth view to the third and the fourth view was in landscape orientation I want everything to rotate back to portrait.

In iOS 5 I simply defined shouldAutorotateToInterfaceOrientation: in each of my view controllers to return YES for the allowable orientations. Everything worked as described above, including the return to portrait even if the device was being held in landscape orientation when returning from view controller #4 to #3.

In iOS 6 all view controllers rotate to landscape, breaking those that weren't meant to. The iOS 6 release notes say

More responsibility is moving to the app and the app delegate. Now, iOS containers (such as UINavigationController) do not consult their children to determine whether they should autorotate. [...] The system asks the top-most full-screen view controller (typically the root view controller) for its supported interface orientations whenever the device rotates or whenever a view controller is presented with the full-screen modal presentation style. Moreover, the supported orientations are retrieved only if this view controller returns YES from its shouldAutorotate method. [...] The system determines whether an orientation is supported by intersecting the value returned by the app’s supportedInterfaceOrientationsForWindow: method with the value returned by the supportedInterfaceOrientations method of the top-most full-screen controller.

So I subclassed UINavigationController, gave my MainNavigationController a boolean property landscapeOK and used this to return the allowable orientations in supportedInterfaceOrientations. Then in each of my view controllers' viewWillAppear: methods I have a line like this

    [(MainNavigationController*)[self navigationController] setLandscapeOK:YES];

to tell my MainNavigationController the desired behavior.

Here comes the question: If I now navigate to my fourth view in portrait mode and turn the phone over it rotates to landscape. Now I press the back button to return to my third view which is supposed to work portrait only. But it doesn't rotate back. How do I make it do that?

I tried

    [[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationPortrait]

in the viewWillAppear method of my third view controller, but it doesn't do anything. Is this the wrong method to call or maybe the wrong place to call it or should I be implementing the whole thing in a totally different way?

15条回答
高级女魔头
2楼-- · 2019-01-02 17:10

You want to Force iOS 6 app portrait only then you can add to a UIViewController subclass below methods

- (BOOL)shouldAutorotate {
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
        return YES;
    } else {
        return NO;
    }
}


- (NSUInteger)supportedInterfaceOrientations {
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
        return UIInterfaceOrientationMaskAll;
    } else {
        return UIInterfaceOrientationMaskPortrait;
    }
}
查看更多
只靠听说
3楼-- · 2019-01-02 17:10

I wanted to have all my VCs locked to portrait orientation except one. This is what worked for me.

  1. Add support for all orientations in the plist file.
  2. In the root view controller, detect the kind of view controller thats on top of the window and set the orientation of the app accordingly in the supportedInterfaceOrientations method. For example, I needed my app to rotate only when the webview was on top of the stack. Here's what I added in my rootVC :

    -(NSUInteger)supportedInterfaceOrientations
    {
        UIViewController *topMostViewController = [[Utils getAppDelegate] appNavigationController].topViewController;
        if ([topMostViewController isKindOfClass:[SVWebViewController class]]) {
            return UIInterfaceOrientationMaskAllButUpsideDown;
        }
        return UIInterfaceOrientationMaskPortrait;
    }
    
查看更多
与君花间醉酒
4楼-- · 2019-01-02 17:11

Add a CustomNavigationController

Override these methods in it:

-(BOOL)shouldAutorotate
{
    return [[self.viewControllers lastObject] shouldAutorotate];
}

-(NSUInteger)supportedInterfaceOrientations
{
    return [[self.viewControllers lastObject] supportedInterfaceOrientations];
}

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
    return [[self.viewControllers lastObject] preferredInterfaceOrientationForPresentation];
}

Now add all orientations in the plist

enter image description here

In the view controller add only the required ones:

-(BOOL)shouldAutorotate
{
    return YES;
}

-(NSUInteger)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskPortrait;
}

these methods override the navigation controller methods

查看更多
只靠听说
5楼-- · 2019-01-02 17:12

Please use the following method to solve this issue

- (NSUInteger)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskPortrait;
}

return only the orientation you want!!!

查看更多
无色无味的生活
6楼-- · 2019-01-02 17:13

After looking through every answer in countless similar questions on SO, none of the answers worked for me, but they did give me some ideas. Here's how I ended up solving the problem:

First, make sure your Supported Interface Orientations in your project's target contain all orientations that you want for your rotating view.

enter image description here

Next, make a category of UINavigationController (since Apple says not to subclass it):

@implementation UINavigationController (iOS6AutorotationFix)

-(BOOL)shouldAutorotate {
    return [self.topViewController shouldAutorotate];
}

@end

Import that category and the view controller that you want to be able to rotate (which I'll call RotatingViewController) to your highest level view controller, which should contain your navigation controller. In that view controller, implement shouldAutorotate as follows. Note that this should not be the same view controller that you want to rotate.

-(BOOL)shouldAutorotate {

    BOOL shouldRotate = NO;

    if ([navigationController.topViewController isMemberOfClass:[RotatingViewController class]] ) {
        shouldRotate = [navigationController.topViewController shouldAutorotate];
    }

    return shouldRotate;
}

Finally, in your RotatingViewController, implement shouldAutorotate and supportedInterfaceOrientations as follows:

-(BOOL)shouldAutorotate {
    // Preparations to rotate view go here
    return YES;
}

-(NSUInteger)supportedInterfaceOrientations {
    return UIInterfaceOrientationMaskAllButUpsideDown; // or however you want to rotate
}

The reason you need to do this is because iOS 6 gives control of rotation to the root view controller instead of the top view controlller. If you want an individual view's rotation to behave differently than other views in the stack, you need to write a specific case for it in the root view controller.

查看更多
其实,你不懂
7楼-- · 2019-01-02 17:15

I'd like to give a partial answer to my own question. I found the following line of code, used in the viewWillAppear method of my third UIViewController, to work:

[[UIDevice currentDevice] 
      performSelector:NSSelectorFromString(@"setOrientation:") 
           withObject:(id)UIInterfaceOrientationPortrait];

But I don't really like this solution. It is using a trick to assign to a read-only property which according to Apple documentation represents the physical orientation of the device. It's like telling the iPhone to jump to the correct orientation in the hand of the user.

I am very tempted to leave this in my app since it simply works. But it doesn't feel right so I'd like to leave the question open for a clean solution.

查看更多
登录 后发表回答