shadowmatter
shadowmatter

Reputation: 1382

Supporting rotation on only a child UIViewController in a hierarchy?

I have a child UIViewController with that's part of a hierarchy with a UITabBarController and a UINavigationBarController. Let's call it ChildViewController; then my hierarchy looks like:

UITabBarController
  |
  UINavigationViewController [tab 1]
    |
    SomeParentViewController
      |
      SomeOtherParentViewController
        |
        ChildViewController

Now I want only ChildViewController to support rotation to landscape orientation. (It's a view controller that shows a chat view, and the landscape mode is easier for typing for some.) I added method - (BOOL) shouldAutorotateToInterfaceOrientation: to ChildViewController to declare that it supports landscape orientation, but rotating the device had no effect. From debugging, I found that – willAnimateRotationToInterfaceOrientation:duration: wasn't being called.

After some searching around online, I've found that a descendent of a UITabBarController only supports a given orientation if the UITabBarController itself supports that orientation. And, strangely enough, UITabBarController only supports an orientation if the view controllers for each of its tabs support rotation. Like tab 1 above, the view controllers for the other three tabs are UINavigationViewController instances; and, because we must go deeper, each UINavigationViewController only supports orientation if its child view controller supports the orientation.

So at this point, adding adding - (BOOL) shouldAutorotateToInterfaceOrientation: to SomeParentViewController and the children of the other UINavigationController instances allowed ChildViewController to rotate. But now SomeParentViewController and the other three tabs will rotate to landscape, and it looks horrible. I only wanted ChildViewController to support landscape.

As a latch ditch effort, I created my own UITabBarController subclass called RotatingUITabBarController and add a global flag to the ChildViewController class that lets me know if it has been created and is displayed. The RotatingUITabBarController overrides only - (BOOL) shouldAutorotateToInterfaceOrientation: and is implemented as:

if ([ChildViewController isDisplayed]) {
  return ((toInterfaceOrientation == UIInterfaceOrientationPortrait) ||
      (toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft) ||
      (toInterfaceOrientation == UIInterfaceOrientationLandscapeRight));
}
return NO;

Now, if I boot the app, switching to SomeParentViewController or any other tab and rotating the phone does not switch to landscape mode, instead keeping in portrait. So far so good. If I create and display ChildViewController and rotate the phone, it enters landscape. So far so good. But now if I pop ChildViewController to reveal SomeOtherParentViewController, it is also in landscape. And so is SomeParentViewController and every other tab that I switch to.

I'm out of tricks now. Any advice would be much appreciated, thanks!

Upvotes: 0

Views: 454

Answers (1)

Caleb
Caleb

Reputation: 124997

Perhaps the best model for the kind of behavior you seem to want is the YouTube app. Most the interface is portrait-only, but the view that plays videos works in either portrait or landscape.

If you look at that app, you'll notice that the whole tabbed part of the UI is actually a modal view controller. When you launch the app, the tab bar controller is immediately presented modally. The only time you leave that modal tab bar controller is when you play a video -- you'll notice that the whole tabbed interface slides down to reveal the video view. When the video ends, the tab bar controller is again presented modally.

This is an inversion of the "normal" approach, where you use a modal view controller only briefly, but it works very well in the YouTube app. It may or may not work well for you too. The important thing is to make your app predictable and fluid, and make the user feel in control at all times.

Upvotes: 1

Related Questions