views:

5567

answers:

7

I have a UITabBarController, and each tab handles a different UIViewController that pushes on the stack new controllers as needed. In two of these tabs I need, when a specific controller is reached, the ability to rotate the iPhone and visualize a view in landscape mode. After struggling a lot I have found that it is mandatory subclassing UITabBarController to override shouldAutorotateToInterfaceOrientation. However, if i simply return YES in the implementation, the following undesirable side effect arises:

every controller in every tab is automatically put in landscape mode when rotating the iPhone.

Even overriding shouldAutorotateToInterfaceOrientation in each controller to return NO does not work: when the iPhone is rotated, the controller is put in landscape mode.

I implemented shouldAutorotateToInterfaceOrientation as follows in the subclassed UITabBarController:

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    if([self selectedIndex] == 0 || [self selectedIndex] == 3)
        return YES;

    return NO;
}

So that only the two tabs I am interested in actually get support for landscape mode. Is there a way to support landscape mode for a specific controller on the stack of a particular tab?

I tried, without success, something like

(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {

if([self selectedIndex] == 0 || [self selectedIndex] == 3)
{   
   if ([[self selectedViewController] isKindOfClass: [landscapeModeViewController class]])
           return YES;
    }

     return NO;

}

Also, I tried using the delegate method didSelectViewController, without success. Any help is greatly appreciated. Thank you.

+2  A: 

This worked for me:

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
    if(self.selectedIndex == 0 && [[[self.viewControllers objectAtIndex:0] visibleViewController] isKindOfClass:[MyViewController class]])
     return YES;
    else
     return NO;
}
Rich Aston
Rich, thank you very much. The proposed solution works like a charm ;-)This problem was driving me crazy! I did not realize that the proper way to do this also requires checking simultaneously for the visibleViewController. Great answer!
unforgiven
A: 

hi, having the same issues i guess. Does this mean I have to put this code above in a sub classed of UITabBarController? thanks

sami
+4  A: 

Here's an extension to UITabBarController that delegates calls to shouldAutorotateToInterfaceOrientation to the currently selected child controller. Using this extension, you don't need to subclass UITabBarController anymore and you can use shouldAutorotateToInterfaceOrientation in your controllers like one would expect.

UITabBarController+Autorotate.h:

#import <UIKit/UIKit.h>

@interface UITabBarController (Autorotate)
@end

UITabBarController+Autorotate.m:

#import "UITabBarController+Autorotate.h"

@implementation UITabBarController (Autorotate)

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
    UIViewController *controller = self.selectedViewController;
    if ([controller isKindOfClass:[UINavigationController class]])
        controller = [(UINavigationController *)controller visibleViewController];
    return [controller shouldAutorotateToInterfaceOrientation:interfaceOrientation];
}

@end
Andreas
Actually this isn't even neccessary. UITabBarController seems to do something similar, but only agrees to orientations, every controller of each tab supports.
Andreas
Andreas, that would be terrific news! But I'm not seeing that in my app. In my case, unless I move along the request to the view controller, the rotation request never comes through. As soon as I put it in, the Tab Bar controller is asked first, then I'm able to propagate to the VC. If there's something I'm missing, please advise!In fact, I'm still having trouble when the VC is within the "More" section of the Tab Bar. In that case, the Tab Bar controller never even gets called. :(
Joe D'Andrea
I've posted my saga in a new thread. http://is.gd/2emkj (redirects back to stackoverflow.com)
Joe D'Andrea
+1  A: 

I've been able to use this for a while now (from my app's tab bar controller) without problems:

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
     return [self.selectedViewController shouldAutorotateToInterfaceOrientation:interfaceOrientation];
}

That way, in the appropriate VC, we get to do the real check, in this case for a photo gallery view (what else?):

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

My gallery view isn't even at the top-of-stack for a given Nav Controller. It still gets called.

Alas, I just discovered that this doesn't work so well when the VC is lurking within the MoreViewController (as opposed to the four main tabs). In that case, my gallery VC never gets called. I think it's because the VC I've been calling all along is really the nav controller from the selected tab, which then propagates things to the appropriate VC, in this case my photo gallery VC. But for the More VC, things don't work so nicely ... aaaand things go rotationally downhill from there. :\

I tried using the modifications by Andreas (see elsewhere in this thread), to no avail. Clues welcome!

Joe D'Andrea
Ah-ha! When I try to rotate the interface, and ONLY when I'm using a VC within the More View Controller, I see this in my console log: "Using two-stage rotation animation. To use the smoother single-stage animation, this application must remove two-stage method implementations." Umm ... news flash, I'm NOT using two-stage animation! That is, I'm not responding to willAnimateSecondHalfOfRotationFromInterfaceOrientation:duration: at all. Apparently the More view controller is, and thus I'm never getting the call to willAnimateRotationToInterfaceOrientation:duration: ... :(
Joe D'Andrea
In fact, if I try to look out for the SecondHalf version ... I don't get that call either. Hmm ...
Joe D'Andrea
+1  A: 

I ran into the same issues as you did when working with the UITabBarController. I needed to control which UIViewControllers were allowed to rotate and which were not. My main problem was with the MORE tab. I did not want any of the UIViewControllers included in the MORE tab to rotate.

My solution was to create my own UITabBarController which I called MyTabBarController:

@interface MyTabBarController : UITabBarController <UITabBarDelegate> {

}

Then I implemented the shouldAutorotateToInterfaceOrientation method:

@implementation MyTabBarController

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
 UIViewController *controller = [self selectedViewController];

 if ((controller == [self moreNavigationController]) || ([self selectedIndex] == 4))
 {
  return interfaceOrientation == UIInterfaceOrientationPortrait;
 }

 return [controller shouldAutorotateToInterfaceOrientation:interfaceOrientation];
}

@end

I needed to discover if the MORE tab was selected. This is a two step process; when the MORE tab is selected initially the API returns a selectedIndex higher than 4 so I needed to compare the selected controller with the moreNavigationController.

If an UIViewController is selected from within the MORE tab then the selectedIndex is finally 4 but the selectedController is not the moreNavigationController anymore but the UIViewController selected.

The if ((controller == [self moreNavigationController]) || ([self selectedIndex] == 4)) takes care of this issue.

Now, when I run my application my UIViewControllers in the MORE tab are not rotated. I hope this will help other developers who are running into the same issues as I did.

Emilio

Emilio Navarro
A: 

the above recepie looks good, however, is there a way to discover which VC inside the more tab is shows so that you can run that VC's shouldAutorotate method directly.

MB
A: 

Thank you, Thank you, Thank you. This has been 2 days in figuring out how to do this. Here is my take on all of your great help when you have a tabBarController with navigationControllers.

-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {

UIViewController *controller = self.selectedViewController;
if ([controller isKindOfClass:[UINavigationController class]])
    controller = [(UINavigationController *)controller visibleViewController];

if([controller isKindOfClass:[LOCviewcontroller class]])
    return YES;
else
    if([controller isKindOfClass:[personWebSiteView class]])
        return YES;
else return NO; 

}

Any critique of a neophite coder's code is always appreciated...jack