What happens under the hood when we do presentViewController? - iphone

Given the below code
self.view.backgroundColor = [UIColor yellowColor];
MyViewController *myVC = [[MyViewController alloc] initWithNibName:#"MyView" bundle:nil]
myVC.view.backgroundColor = [UIColor clearColor];
myVC.modalPresentationStyle = UIModalPresentationFullScreen;
[self presentViewController:myVC animated:NO completion:nil];
What happens under the hood when we call presentViewController ? When myVC is visible I cannot see yellow color, then I checked myVC.view.superView in it's viewDidAppear method and it is UIWindow.
Q1. Is that mean until the modal window is up presentingViewController.view (self.view in above case) is removed from the View hierarchy and presentedViewController.view (myVC.view in above case) is added over UIWindow ?
Q2. What will be the case if myVC.modalPresentationStyle != UIModalPresentationFullScreen ?
Q3. Does iOS also remove all the views from UIWindow except presentedViewController.view until the full screen modal dialog is up for optimization ? If NO why not ?

First, let's discuss the case without animation.
Before calling present:
Your window has one view hierarchy, starting from its rootViewController view.
After calling present:
The view hierarchy still exists without change.
A special full-screen view called "dimming view" is added to the window (that is, not inside the rootViewController's view but inside the window (window is a UIView, too). This view is transparent, dims the presenting controler and blocks user interaction.
The presented (modal) controller's view is then added also to the window.
There are some other views added in between the window and the presented controller's window. If you log your view hierarchy, you'll see classes named _ControllerWrapperView or something similar. However, this has changed between iOS versions and you shouldn't rely on the view structure.
Note that that the modal controller can't ever be transparent because it is not direct subview of the window and the wrappers between the controller and the window are not transparent.
The animated case is almost the same. Only there are some fancy animations between the steps.
Edit 2:
The answer was really a bit incorrect. There is a big difference between iPhone and iPad presented controllers.
On iPhone, the presented controllers are always displayed full screen and the presenting controllers are actually removed from the window.
On iPad, if the presented controller is not fullscreen (see UIModalPresentationStyle), the presenting controller stays in the window.
Your questions:
Is that mean until the modal window is up presentingViewController.view (self.view in above case) is removed from the View hierarchy and presentedViewController.view (myVC.view in above case) is added over UIWindow ?
If the controller is full screen, then this claim is true. Otherwise, the presenting view controller stays there but the whole contents are overlapped by other views (even if they are semi-transparent). Also, there are always some views between the presented and the presenting controller views.
What will be the case if myVC.modalPresentationStyle != UIModalPresentationFullScreen ?
See the answer to the previous question - on iPhone, there would be no difference.
Does iOS also remove all the views from UIWindow except presentedViewController.view until the full screen modal dialog is up for optimization ? If NO why not ?
From my tests, only the presenting controller is removed from the window hierarchy. This is probably to optimize drawing performance. This is the only controller the system can safely remove. Removing any other view could cause problems (e.g. views that should be always visible).
Edit:
If you want to make a transparent controller, you can:
Add the view directly to your view hierarchy (either to the controller's view or to the window) with a transition animation (+[UIView transition...])
The same but also adding a child controller to your controller.

Related

Display whole ViewController within another ViewController's view

Im writing an application which the main view controller is a UIViewController. It has some icons in a grid and I want to dismiss (sliding down) this grid when one of the icons is clicked. This I've done already. The problem is: when the grid is dismisseed I want another View to come from the top of the screen. This view is in this same root view controller. But I want to display the content of other view controllers in this view. For example: I want this view to show a UINavigationController with a UITableView inside it, so the user can navigate through TableViews.
I'm doing this:
HorariosViewController *horarios = [[HorariosViewController alloc] init];
[vuashView addSubview:horarios.view];
HorariosViewController is a UINavigationViewController. It shows me only a blue NavigationBar and changes like self.navigationItem.title = #"Title" won't work.
Thanks!
You can show another view controller's views as subviews but their outlets and actions remain linked to their original view controller unless you write code to make new connections, so self.whatever shouldn't be expected to affect the other view controller's properties.
(Also, if HorariosViewController is a UINavigationController, it shouldn't be created as a UIViewController.)
One approach is to have the navigation controller already there, with the icon grid presented modally on top of it. (you can set the view up this way without animations, so the user doesn't see the navigation controller underneath).
Then, when it's time for the grid to go away, it can call dismissModalViewController on itself with animation.

Switch between UIViewControllers using UISegmentedControl

I have a tabbar -> navigationcontroller structure. In one of these tabs, I want to switch between two UIViewControllers (a KalViewController and a UITableViewController to be be exact), using a UISegmentedControl located in the Navigation Bar.
Currently, I have a third UIViewController, that pops and pushes the appropriate ViewControllers on segment value change. I don't think thats the right way to do it and it also destroys the navigation stack (when I tap on the bar item, the navigation controller goes the root controller, which won't work). And there's even another bug, related to the Kal Component.
So, what's the right way to do it?
The right way to do it is to have the controller handling the UISegmentedControl add the views of the controllers as subviews.
[self.view addSubview:controller.view];
It's your responsibility to send viewWillAppear: and so on.
EDIT: The offset you're talking about can be adjusted using:
controller.view.frame = CGRectMake(x, y, width, height);
EDIT 2: In response to tc.'s comment:
From the documentation of UISplitViewController:
Message Forwarding to Its Child View Controllers
A split view controller interposes itself between the application’s window and its child view controllers. As a result, all messages to the visible view controllers must flow through the split view controller. This works generally as you might expect and the flow of messages should be relatively intuitive. For example, view appearance and disappearance messages are sent only when the corresponding child view controller actually appears on screen. Thus, when a split view controller is first displayed in a portrait orientation, it calls the viewWillAppear: and viewDidAppear: methods of only the view controller that is shown initially. The view controller that is presented using a popover does not receive those messages until the popover is shown or until the split view controller rotates to a landscape orientation.
This is not magical and there is no reason why you wouldn't be able to write a similar controller yourself. In fact I've done it and it worked just fine.

Iphone UIViewController to UINavigation controller programmatically

I have been stuck on this for a few days now and it is killing me... In my viewDidLoad event, I am trying to programmatically add a full screen UINavigationController to a subview of my view controller. So far, I have only succeeded in doing two things...
1) Only a grey screen shows up
OR
2) I get something that resembles a navigation controller added to the view controller, instead of being my navigation controller from a XIB it is just a generic one... even though I loaded from the XIB. Oddly enough it is always shifted 25 pixels downward and slightly cut off.
I have read every single link on google and I can't seem to figure this out. I just created a new viewcontroller... added a UINavigationController to it... try to load that view controller and it messes up.
Any help is greatly appreciated!!!!
Instead of having the UINavigationController be a child of some other view controller, make the UINavigationController the root controller itself. The navigation controller is one of the special "container" view controllers, and it generally wants to own the whole screen and be at the root of the controller hierarchy (except in certain circumstances).
Try something like this:
UINavigationController * rootNavController = [[UINavigationController alloc] initWithRootViewController:myRootControllerInTheNavController];
[window addSubview:[rootNavController view]];
Which will obscure any existing views with the nav controller (those existing things will still be there when you -removeFromSuperview the nav controller's view). The nuclear option is to set your UIWindow's rootViewController property with the nav controller, but it sounds from your comment that this may not be what you want to do here.
Possibly a cleaner approach: If it accomplishes what you want, I believe you could also take your nav controller and present it modally (see docs for uiviewcontroller) from whatever the current view controller is. Set the transition appropriately, and while you're in the nav stack, the nav controller will be visible.

Switching Views within UITabBar View

I have created an UITabView application. Each view selected from the bar is a seperate controller with own nib file. I switch between them succesfully.
In the first view I have two buttons (check out the screenshot). When clicking them I want to switch to another views which are the parts of the current view controller. I use:
[self presentModalViewController:anotherViewController animated:NO];
That switches the view, but hides the UITabBar. How to keep the bar on the screen after the switch?
P.S. Sorry for the blurred image. I am not allowed to share to much info.
Well I think you are misusing the modal view controller. For a problem like this I'll say you should put them in a view controller stack using UINavigationController. Instead of making each tab a UIViewController make it a UINavigationController, then you can push and pop view controllers on it, which still show the tab bar.
See http://developer.apple.com/iphone/library/documentation/UIKit/Reference/UINavigationController_Class/Reference/Reference.html
use: tabBarController.selectedViewController = newViewController
edit: UINavigationController is not needed here.

Displaying UIImagePickerController within another UIView

I've been working pretty extensively the last couple months with UIImagePickerController, particularly with the new capabilities in OS3.1 and newer to overlay views on-top of the camera view. This has worked just fine.
However, I am currently working on a project where I'd like to be able to display the camera view of the UIImagePickerController within an existing view. Essentially, the exact opposite of what I've currently been doing.
An example would be a View Controller with navigation components (Think top and bottom horizontal bars with gradients), and upon tapping a button on one of these bars, then content area displays the camera view. The shutter animation would should up, and the top and bottom navigation bars would remain always on-top.
I've had success adding the UIImagePickerController to the window view, as well as presenting it modally, but haven't had any luck adding it as a subView.
ex:
[window addSubview:camera.view];
[self presentModalViewController:camera animated:YES];
All you need to do is call viewWillAppear and viewDidAppear.
Here is an example where _pickerController is an instance of UIImagePickerController:
[self.view addSubview:_pickerController.view];
[_pickerController viewWillAppear:YES];
[_pickerController viewDidAppear:YES];
Call viewWillAppear:YES on the image picker controller after adding its view to your view. Skip the modal view controller business.
I don't think the API provides direct access to the actual view of the UIImagePickerController. The class is actually a subclass of UINavigationController so I don't think it has an actual view itself but rather manages the calling of its subcontrollers and their views.
When you call the UIImagePickerController modally, its doesn't add the views it controls as subviews to the window (or any other view). That is what a modal view means. It presents the view off to the "side" of the view hierarchy.
Even if you could hack this together, I think Apple would reject it as not being part of the API and for violating the HIG.