简体   繁体   中英

ViewController in UINavigationController orientation change

So I have the following hierarchy:

UINavigationController --> RootViewController ( UIViewController ) --> UITableViewController --> DetailViewController ( UIViewController )

I want to lock the orientation on RootViewController to Portrait only, but leave all orientations for the rest view controllers.

If I put this to subclassed UINavigationController :

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    // Return YES for supported orientations
    return (interfaceOrientation == UIInterfaceOrientationPortrait);
}

-(NSUInteger)supportedInterfaceOrientations{
    return UIInterfaceOrientationMaskPortrait;
}

All view controllers are then locked to portrait.

My question is, is there a way to lock only RootViewController to Portrait, but leave all options for other view controllers?

check the link here for fixing autorotation in iOS 6 and set orientation support per view basis: http://www.disalvotech.com/blog/app-development/iphone/ios-6-rotation-solution/

Here is what you could do:

  1. Create a custom navigation controller that is a subclass of UINavigationController , in your .m file:

      - (BOOL)shouldAutorotate { return self.topViewController.shouldAutorotate; } - (NSUInteger)supportedInterfaceOrientations { return self.topViewController.supportedInterfaceOrientations; } 
  2. In your AppDelegate.h ,

      @interface AppDelegate : UIResponder <UIApplicationDelegate> { UINavigationController *navigationController; ViewController *viewController; } @property (strong, nonatomic) UIWindow *window; @property (strong, nonatomic) ViewController *viewController; 

    and in AppDelegate.m ,

      - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // set initial view self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; viewController = [[ViewController alloc] initWithNibName:@"RootViewController" bundle:nil]; navigationController = [[CustomNavigationController alloc] initWithRootViewController:viewController]; // iOS 6 autorotation fix [navigationController setNavigationBarHidden:YES animated:NO]; self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; [self.window setRootViewController:navigationController]; // iOS 6 autorotation fix //[self.window addSubview:navigationController.view]; [self.window makeKeyAndVisible]; return YES; } - (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window // iOS 6 autorotation fix { return UIInterfaceOrientationMaskAll; } 
  3. in your rootViewController, for whatever the event push the second view controller, do this:

      - (IBAction)pushSecondViewController:(id)sender { // push second view controller SecondViewController *secondViewController = [[SecondViewController alloc] initWithNibName:@"SecondViewController" bundle:nil]; [self.navigationController pushViewController:secondViewController animated:YES]; } 
  4. in your each view controller, add

      - (BOOL)shouldAutorotate{} - (NSUInteger)supportedInterfaceOrientations{} 

    for iOS 6, you can set each view controller whatever the orientation support you want individually.

    for iOS 5 and below, you can set

      - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation{} 

All the credits goes to John DiSalvo who wrote the sample app in the link.

Hope this helps.

in singleton

-(void)setOrientationSupport:(BOOL)flag{

    flag_orientationSupport_ = flag;
}

-(BOOL)getOrientationSupport{

    return flag_orientationSupport_;
}

in appdelegate

- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{

    if ([singleton getOrientationSupport])
        return UIInterfaceOrientationMaskAll;
    else
        return UIInterfaceOrientationMaskPortrait;
}

add the following code in viewwillappear

for the viewcontroller you want support orientation

[singleton setOrientationSupport:YES];

to those controller you want disable orientation

[singleton setOrientationSupport:NO];

Put this in your Nav controller:

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

-(NSUInteger)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskAll;
}

Now, add this to your root view controller:

- (BOOL)shouldAutorotate
{
    return NO;
}

That should do it!

To delegate the responsibility of defining allowed orientations to subviews of a UINavigationController, one can use the visibleViewController property of the nav controller to make the navigation controller "ask" it's child views for their supported orientations. The following code should work (Swift) :

  1. Sub-classed navigation controller:

     override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask { if let visibleView = self.visibleViewController { return visibleView.supportedInterfaceOrientations() } else { return UIInterfaceOrientationMask.All } } 
  2. Sub-view (root view) of nav controller:

     // Restrict to portrait override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask { return UIInterfaceOrientationMask.Portrait } 

I have been searching for the solution for hours!

So after implementing the needed methods everywhere. shouldAutorotate doesn't need to be set to YES because it is already set as default:

- (NSUInteger)supportedInterfaceOrientations{
    return UIInterfaceOrientationMaskPortrait;
}

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation{
     return UIInterfaceOrientationPortrait;
}

When it is time to show the UIViewController which needs the orientation different than the other views, I created a UIStoryboardSegue with this implementation inside:

#import "Showing.h"

@implementation Showing

- (void)perform{
    NSLog(@"Showing");
    UIViewController *sourceVC = self.sourceViewController;
    UIViewController *presentingVC = self.destinationViewController;

    [sourceVC.navigationController presentViewController:presentingVC 
                                                animated:YES 
                                              completion:nil];
}

@end

Inside the UIStoryboard I connected the views with this segue ( showing ):

在此输入图像描述

It is just important, you are using

presentViewController:animated:completion:

AND NOT

pushViewController:animated:

otherwise the orientation won't be determined again.


I had been trying things like

[UIViewController attemptRotationToDeviceOrientation];

OR this one inside the UIViewController where the orientation should change, and I also tryied to call it inside my custom UIStoryboardSegues before presentingViewController and dismissViewController :

NSNumber *numPortrait = [NSNumber numberWithInt:UIInterfaceOrientationPortrait];
[[UIDevice currentDevice] setValue:numPortrait forKey:@"orientation"];

OR

 NSNumber *numPortrait = [NSNumber numberWithInt:UIInterfaceOrientationPortrait]; [[UIDevice currentDevice] setValue:numPortrait forKey:@"orientation"]; 

But no one of them worked. Of course the last example shouldn't be an option, because if apple will change anything of their api this could cause problems inside your app.

I also tried to use the AppDelegate method and always determine the orientation inside this method after looking for the correct UIInterfaceOrientation of the actual visibleViewController but then it sometimes happened to crash when switching from one to another orientation. So I'm still wondering why its made so complicated and there seems also not to be any documentation where it is explained correctly. Even following this part didn't help me.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM