views:

557

answers:

3

Hello, I did some experiments on autorotation. The situation: I have a TabBar 4(tabs), three should be portrait only. The last one is a UINavigationController, which by itself should not autorotate any of the stacked controllers. It is basically a browsing application, as I show file and folders everything should be portrait. Some times, a special UIViewController is pushed, and I would like only this one to autorotate (it is always the last on the stack). In this last view, the tabbar is hidden.

How I achieved the goal: I subclassed the UITabBarController, to override the standard shouldAutorotate method behaviour:

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

This way, the answer of shouldAutorotate is forwarded to the controlled tabs, and in particular for the UINavigationController, it is again delegated to the visible UIViewController. Basically this works, as I have all the UIViewControllers answering NO, except for the particular one I described above: correctly, when rotating the Simulator, only when the special UIViewController is visible, the interface rotates to landscape, whici is perfect. The Tabbar here is hidden, so user don't get that also that one is rotated (which would be unconsistent in my design: basically whenever the tabbar is visible, which means everywhere except in this special view, the application is portrait only).

The problem is that I would like that, even if the device is still in landscape mode and user pops the special ViewController, the interface should behave consistently and return to portrait mode. Instead, when I pop, the interface stays in landscape (it's not designed in that way so it's a mess, of course) even when showing a UIViewController that would answer NO to shouldAutorotate... this is because (I think) the method is called only when rotation occurs, so until the rotation actually occurs again, the interface is rotated to landscape anyway.

How do avoid this? My first solution would be somehow to intercept the popping of the last view, and rotate manually the view before popping... but I'm not sure, I hope there is some more robust method to handle!!

I use the simulator with 3.0, dunno if this makes a difference.

A: 

The problem is that you are rotating your UITabBarController (with the child view controller on top of it) rather than just the child view controller. You should be able to implement shouldAutorotateToInterfaceOrientation: only in your child view controller and have it work properly. It would also simplify your code.

I have never had this issue, but I've also never implemented shouldAutorotateToInterfaceOrientation: in a "container" view controller like a UITabBarController or UINavigationController.

Frank Schmitt
Yes you're right... But I came to this as I tried to set the autorotation only in the child view controller, but it seems that this does not work. I started overriding the shouldAutorotate only in the last view controller... nothing: : the shouldAutorotate is called (I used NSLogs to see), but the willRotate is never called. Then I extended to all the stacked view controllers, but nothing again. Then I subclassed the UINavigationController itself to allow autorotation, but no... Only when i subclassed the TabBar to allow autorotation, everythings started to rotate.
Andy
Weird :/. If you have the time you might write a minimal repro case of this and submit it to Apple as a bug report.
Frank Schmitt
+1  A: 

I know that this is not a solution to your problem, but I think you should really avoid this kind of user interface when portrait-only portrait+landscape or landscape-only pages mixed on the same UINavigationController. Unfortunately the rotation management is extremely buggy and the bugs vary on different firmware versions.

I managed to quite the same thing in one of my projects, but had to remove it later due to firmware bugs: for example if you pressed the "back" button in landscape mode and went back to a portrait-only view, it often occured that the status bar and/or the navigation bar remained in landscape mode and the layout was completely broken. As far as I know this bug is not yet fixed although it was already present in firmware 2.x.

If you still want to do this I suggest the following things:

  1. Make sure that all overridden UIViewController methods (init, viewWillAppear, etc) calls its [super methodName]. If not, auto-rotation is silently buggy. This was mentioned in the "Getting Ready for iPhone OS 3.0 Technical Note" (https://developer.apple.com/iphone/checklist/), but currently this document is unavailable :(

  2. You may experiment with calling the undocumented [UIDevice setOrientation:] method when leaving the landscape view. It sometimes needs to be called twice, once with the current orientation and once with the desired orientation :) You may also need to call [UIDevice setStatusBarOrientation:] if the status bar remains in landscape mode. But note that Apple is likely to reject your application if you use these methods (they introduced an automatic tool some time ago which detects the presence of undocumented symbols in your application).

gyim
A: 

The problem with your implementation is that you use the visibleViewController member of UINavigationController. You should use topViewController instead and everything will work as expected.

Werner Altewischer