popViewControllerAnimated: From Landscape To Portrait - ios

I'm having trouble popping a UINavigationController (subclass) from a landscape view to a portrait view.
I have a chain set up from the window's rootViewController using shouldAutorotate and supportedInterfaceOrientations down to the individual View Controllers that I present.
So, I have a View Controller that ONLY supports portrait. The next one that is pushed supports landscape as well.
When I push the one that supports landscape and then rotate the device, everything rotates. So far, so good.
Now, when I pop back to the first one, both views rotate BEFORE the animation. So, the second viewController's landscape view (which is really small because of the rotation transform) is pushed away to the right side of the portrait screen.
I want the landscape view to be pushed away to its right (the top or bottom of the portrait view controller), while the portrait view controller is shown in the background.
How can I accomplish this?
I thought I might try to use an animation controller, but the UINavigationController's delegate method, navigationController:animationControllerForOperation: isn't called when popping to a View Controller in a different orientation.

After days of experimentation I figured out what my problem was. Hopefully this will help someone in the future.
I was trying to over-engineer the chain.
I had a custom container with a child tab bar controller which contained multiple navigation controllers that contained view controllers with different orientation requirements.
My main downfall was assuming that even if a modal window was presented, the system would still ask the window's root view controller first. To account for this situation I added a check in the container that looked like this:
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
if (self.presentedViewController) { // This check is wrong! Don't do this!
return [self.presentedViewController supportedInterfaceOrientations];
}
// Pass request to child view controller
}
However, this was fundamentally causing my problem in two ways.
First, without this check, UIKit will automatically detect the modal view controller and directly ask for its supportedInterfaceOrientations.
Second, the way UIKit handles the animations involves presenting several internal view controllers that we normally would have no idea are there. By checking for a modal view controller in presentingViewController, the check was catching them and asking them all for their supportedInterfaceOrientations. UIKit isn't meant to behave this way, so I was the one interfering with its operations.
The fix was to implement supportedInterfaceOrientations so that it ONLY concerns the view controller's direct descendants (i.e. the view controllers whose views are descendants in the current view controller's view hierarchy). The direct descendants can further pass the request down from there.
Treat modal windows as a completely separate chain, regardless of the presenter.
In other words, trust UIKit.

Related

With a custom navigation controller push transition, how do I have the previous view controller behind like modal presentations?

When performing a custom modal view controller transition, the view controller you're coming from sits behind the new one nicely (think of Apple's "form sheet" style presentation on an iPad for instance), and when you rotate the device the previous view controller visible in the back rotates as well.
I'm unsure how to get this functionality with a UINavigationController custom push animation. It seems it isn't expected for the previous view controller to be visible from behind and it isn't.
I could take a screenshot, but it won't update on landscape rotation.
How is it done so easily with a modal transition and how do I replicate that for navigation controller custom transitions?
As far as I understand the UINavigationController class such functionality cannot be achieved through it.
UINavigationController is a container controller, which can show only one VC within it at a time. It keeps all the VCs in the stack, but not their views (views are kept by VCs themselves).
Unlike it, the modal presentation is a special type of VC-presentation, and it's not limited by the container-functionality.

willRotateToInterfaceOrientation not being called if not visible

I have the following structure (iOS 7 app):
UIWindow -> UITabBarController -> 2 Tabs
Tab1: NavigationController with HomeViewController as root.
Tab2: NavigationController with OtherViewController as root.
If I rotate the iPad while being in Tab1, HomeViewController executes WillRotateToInterfaceOrientation: without problem. But if I'm in Tab2 and rotate the iPad, willRotate: method of HomeViewController in Tab1 doesn't execute, so when I go back to Tab1 the view's layout is in the wrong orientation and messed up.
What's happening? Thanks in advance for your knowledge.
This is the expected behavior on iOS, since HomeViewController was not being shown while the device was rotated.
Checkout the Apple documentation for supporting multiple view controller interface orientations.
Specifically, the section Rotations May Occur When Your View Controller Is Hidden, that reads:
If your view controller’s contents are not onscreen when a rotation occurs, then it does not see the list of rotation messages. For example, consider the following sequence of events:
Your view controller presents another view controller’s contents full screen.
The user rotates the device so that the user interface orientation changes.
Your app dismisses the presented view controller.
In this example, the presenting view controller was not visible when the rotation occurred, so it does not receive any rotation events. Instead, when it reappears, its views are simply resized and positioned using the normal view layout process. If your layout code needs to know the current orientation of the device, it can read the app object’s statusBarOrientation property to determine the current orientation.
So basically you have to prepare your view controller to update itself according to rotations that may have happened while it was not listening to notifications.
The most common way to do this is to place interface-specific code on viewWillAppear:, since this method is called every time your view controller is shown onscreen.

Disabling rotation for ViewController in NavigationController

Is it even possible to disable rotation when the View Controller I want to disable it in is
in a Navigation Controller that supports all directions? for one special view I want only portrait mode to be allowed, I´v tried about everything but nothing seems to work - I am guessing this is cause the View Controller is part of a Navigation Controller with segues?
By Default any property or a setting of a Super View is applied on all the sub-views inside it. Therefore even if you block the View Controller it will rotate because it is in Navigation Controller that allows rotation. Therefore i would suggest that you create a property in your AppDelegate that sets the orientation settings and then haldle it from ViewControllers

Keep one view orientation when rotating

I have the app with two view controllers.
The first view controller contains two subview: one would keep it's orientation; second - would be rotated (like camera layer and controls in iOS Camera app).
The second – should support all orientations.
I've found a solution: to keep first view controller in Portrait mode and rotate the subview manually, by handling UIDeviceOrientationDidChangeNotification.
The problem is in iOS 6.0.
I've tried to add category for UINavigationController, but it seems like rotation rules are global.
A sample code is attached
https://dl.dropbox.com/u/2167984/temporary/rotationSample.zip
I've resolved this issue: just moved first view controller out of navigation controller and show the second view controller as modal.

Can a UISplitViewController be the root controller in a UINavigationController?

Interface builder does not allow you to add a UISplitViewController as the root controller of a UINavigationController.
I've also tried programmatically creating the UINavigationController and setting its root view controller to be the UISplitViewController.
The result is an empty window with just the nav bar.
I've also tried a split view controller replacement, MGSplitViewController. It mostly works, except that within the split view controller, the master view is another UINavigationController. Its nav bar shows up too thick. Changing orientation and back clears it up.
I've been trying all sorts of different approaches to having a view that looks like a split view and other views that I switch between. I've tried within a tab view controller, writing my own controller to manage subviews of the window and having the split view as a managed view, and now the navigation controller. All attempts have had some issues. The most consistent issue is regarding the orientation of the view. My app is running in landscape mode and typically the child views think its still portrait.
Any ideas appreciated.
No.
The bottom line: a UISplitViewController must be the root view of an app (or perhaps more specifically, a window). It can not live inside a UINavigationController or anything else.
This is the case with the current SDK, and there's no guarantee that will change in future SDKs.
It seems strange to add a split view to a navigation stack. The master pane of a split view controller is generally a navigation controller, so (without knowing more about your design), I'd probably use that to control your navigation hierarchy.

Resources