view controller hierarchy and device orientation changes - ios

When orientation changes occur and view controllers are subsequently notified of changes, does the entire of the root view and all its subviews receive these notifications? I have created a root view and a subview to that root view. Does the subview's controller (and any controllers of subviews in this hierarchy) receive all the rotation handling resulting from device orientation changes?
I ask because nested in this hierarchy is a UISplitViewController's view, and I suspect it is not receiving notification of device orientation changes. i.e. I essentially have something like A -> B -> C in my view hierarchy where C is a UISplitViewController's view.

When orientation changes occur only the first view of the window will rotate together with its entire hierarchy.
In terms of viewControllers, the first view's viewController will get the orientation change events (and viewWill/DidAppear events) and all the viewControllers spawn by it. By spawn I mean a viewcontroller displayed using either as a tab of a uitabbarViewController, or added to a navigation controller, or displayed modally.
In other words if somewhere in your hierarchy you added a viewcontroller's view manually to the window or another view, that viewcontroller will not get any events.
See this: why does viewDidAppear not get triggered?
same thing happens for orientations changes
So there are 2 possible causes:
your uisplitviewController's view or any of its parent is added manually to the view hierarchy instead of pushing the viewcontroller (the view gets rotated but does not receive events)
you added the view as a second child of the window (the view doesn't get rotated at all)

Related

popViewControllerAnimated: From Landscape To Portrait

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.

IOS: Disable scroll of a superview in IOS

All I am working on a legacy code that does the following
creates a child view controller and add's it to the parent controller
The child view controller is presented in the parent VC. So the animation starts from bottom towards the top, but only up to a specific location, leaving a height of 100 from the top.
So I have a superview being displayed within the bounds (0,0), to (SCREEN_WIDTH, 0) and (0, 100) and (SCREEN_WIDTH, 100)
The child view controller view is displayed below it.
If you tap on any part of the screen that is a part of the parent view, you can swipe and up down, causing it to scroll. I don't want that. How do I disable scrolling of the superview when the child view is loaded?
I have tried the following solutions.
Create a delegate protocol that parent VC implements to try to set the content off set to CGPointZero. This delegate is called from the child view.
Create a delegate protocol that parent VC implements and call the following function from the parent VC (which has a custom UIScrollView) that executes the following
self.view.scrollEnabled=NO;
This option did not work for me either. Is there any other way for me to do this?
Present a child view controllers view fully with clear color . Inside that view make a subview by doing like this " but only up to a specific location, leaving a height of 100 from the top." so nothing is touchable from the parent's view. Or do what ever your doing ,but when child is about to present disable parent controller view with user interaction enable to 'NO' or cover it with clear color view.When child is diss missed enable the parent controllers view or remove that cover view.

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.

Equivalent PreferredContentSize when using ChildViewControllers

Am trying to switch over from using UIPopoverController to ChildViewControllers on iPad. We have 4 or 5 VC's that navigate within the parent nav subview controller, all with different sizes, each time we push or pop, the parent popover resizes based on the PreferredContentSize for the vc. Now have switched over to AddChildViewController, the parent vc keeps same size, is there an equivalent PreferredContentSize for ChildViewControllers?
thanks
No, there isn't. Child view controllers are used by your custom container view controllers. It is the responsibility of the container view controller to specify what it wants from its children and to gather and act on that information.
Thanks.
Discovering there are quite a few things don't get for free with childviewcontrollers that you get with popovers, like autoresize/reposition when keyboard is shown, am doing this manually now too.
Reason needed to update, is need a full screen modal view controller over the top of the pop over view controller, so thinking was change over to child view controller and can have multiple child view controllers, where as with popover you are limited to the one on the screen, tried mixing the two but popovervc is always on top.

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.

Resources