views:

2518

answers:

4

I have a UIViewController that returns YES in shouldAutorotateToInterfaceOrientation: for UIDeviceOrientationPortrait and NO for everything else. With that view on the top of the stack, I use pushViewController:animated: to push a new UIViewController. The new controller returns YES for anything in shouldAutorotateToInterfaceOrientation:.

The first view refuses to rotate (as expected). Once the second view is pushed, the user can rotate the device and the UI will rotate (also as expected). If the second view is in landscape mode and the user presses the back button (which calls popViewControllerAnimated:), the first view will appear rotated (unexpected!).

If the user rotates the device back to portrait orientation, the view will rotate and then be stuck in portrait mode as before. This works but it's ugly for the user until they rotate back. So I'm in search of a way to make this view stay in portrait mode.

The only workaround I have found so far is to use -[UIDevice setOrientation:], which throws a warning (orientation is read-only) but works since it is actually defined. This is a huge hack and I'd like a real solution. In search of a real solution I attached GDB to the Photos application (MobileSlideshow.app) and discovered that it too uses -[UIDevice setOrientation:]. Being an internal application though I guess they have different rules.

Is there a correct way to achieve the expected autorotation behavior?

A: 

I was about to tell you that there was probably no way, but then I had a thought. It would be difficult to get right, but you might be able to make it work if you used two separate UINavigationControllers: one that controls the root view and prohibits rotation, and one for the child views that allows it. You would manually handle the transition to and from the root controller and the child controller.

You'd have to patch up the child navigation controller to have the correct back button. And, of course, you'd have to handle the back button press yourself. You would probably have to use a dummy UINavigationBar to do the animation from one navigation controller to the next so that the transition will look right. You would also have to animate the "push" transition between the navigation controllers, as well, which might take a bit of tweaking to get it to look right. You would have to:

  1. Configure a dummy navigation bar to exactly match the outgoing navigation controller's and place it directly on top of the navigation controller's bar. (You could copy the configuration of the current view controller's UINavigationItem and push it on)
  2. Place the new navigation controller off-screen at the right edge
  3. Animate the movement of the new and old controllers' frames from right to left
  4. Create a copy of the UINavigationItem for the incoming view controller and push it on the dummy navigation bar
  5. When the animation completes, remove the dummy UINavigationBar from the view, and also the outgoing navigation controller.

All of this is a lot of work, but if you're very clever (and very tenacious), you might be able to get it to work. I'd love to see the result!

That said, you might be better off just using setOrientation: and taking your chances with the App Store approval process ;-)

Alex
+1  A: 

Try it again with the 3.0 OS (now that we can talk about it). Two special cases of this have been worked out under 3.0:

  1. Presenting a portrait modal view over a landscape view and vice-versa. An example is the pre-3.0 behaviour in Mail for viewing attachments. You could rotate to landscape for a PDF and get back the portrait view for the message when you dismissed the attachment view. (Now that we have landscape message view in 3.0 this behaviour seems to be gone).

  2. Mixing orientations within a view stack and getting the correct orientation back when you pop the stack. An example is the transition between the tableview of movies and the movie view in the YouTube app.

There seem to have been some thorny aesthetic problems of how the default transitions for all the permutations should look. There are some weird elements if you view in slo-mo, but it beats bending over backwards in your own code.

Ryan McCuaig
I tried this and #1 does seem to work correctly, but #2 isn't working for me. I set up a nav-controller project with table view controllers. The root view can't rotate, but the pushed view can. When you pop the second view, it'll stay rotated. In fact, the nav-bar-items appear to get confused, showing the title of the second view even after popping it off.
Daniel Dickison
A: 

I've been playing with this recently as well, often I had just handled rotations myself by listening to the deviceDidRotate notification and then handling the window frame and transform, as well as setting the statusBar orientation appropriately.

However this morning I found that UINavigationController forces the correct orientation when you hit the back button. So, although still a bit ugly, you could push the viewController with the limited orientations twice and pop it twice non-animated to force your orientation, and then pop it animated once to display it.

Not ideal, but it's better than trying to handle it all yourself, which often ends up in a HUGE mess.

gypsyOtoko
A: 

So this annoying bug comes a really long way from iOS 1 to iOS 4.

I believe the best solution we have it duplicate this bug and let Apple know we really want it fixed. I've just reported it again under the bug ID 8478525.

an0