I am trying to present a UIViewController with a UIView on it.
The following is the code I am trying in my viewDidLoad method.
//create the view controller
UIViewController *controller = [[UIViewController alloc] init];
UIView *view = [[UIView alloc] init];
view.backgroundColor = [UIColor whiteColor];
controller.view = view;
//show the view
[self presentViewController:controller animated:YES completion:nil];
When I run the app, it is giving me the following error.
Warning: Attempt to present <UIViewController: 0x751fcd0> on <ViewController: 0x751d7a0> whose view is not in the window hierarchy!
What does this mean and where am I going wrong? Shouldn't it display a white view or am I understanding wrong?
Thanks.
The solution is to move my code to the viewDidAppear method.
I'm assuming that the view controller's view is not in the window hierarchy at the point that it has been loaded (when the viewDidLoad message is sent), but it is in the window hierarchy after it has been presented (when the viewDidAppear: message is sent).
If you call presentViewController:animated:completion from 'viewDidLoad:' it won't work. And that is why:
The area of the screen used to define the presentation area is determined by the presentation context. By default, the presentation context is provided by the root view controller, whose frame is used to define the frame of the presentation context. However, the presenting view controller, or any other ancestor in the view controller hierarchy, can choose to provide the presentation context instead. In that case, when another view controller provides the presentation context, its frame is used instead to determine the frame of the presented view. This flexibility allows you to limit the modal presentation to a smaller portion of the screen, leaving other content visible.
View Controller Programming Guide for iOS: Presenting View Controllers from Other View Controllers
In viewDidLoad frame of presenting view controller simply not set yet. That is why you should present next controller only when presenting controller is on screen.
Related
I have a UIPageViewController subclass that shows images. This view controller is inside a larger view controller that has other content. I want to be able to tap on an image in the page view controller and have that page view controller removed from where it is and presented full screen, where additional controls such as zooming and panning around the image would be available. Then, I also need a way to be able to dismiss it from being presented full screen and to re-insert it in the original parent view controller.
- (void)handleTapGesture {
UIViewController *parentViewController = self.parentViewController;
[self didMoveToParentViewController:nil];
[self.view removeFromSuperview];
[self removeFromParentViewController];
self.modalPresentationStyle = UIModalPresentationFullScreen;
[parentViewController presentViewController:self animated:YES completion:nil];
}
But when I do this, I can see the dimming view and everything that is set up automatically when presenting the view controller, but the view controller itself is not visible.
I viewed it in the view debugger, but it looks like the frame of the page view controller is zero sized. Here is some output from the debugger:
Unbalanced calls to begin/end appearance transitions for <MyPageViewController: 0x10ca8f000>.
Printing description of $21:
<_UIPageViewControllerContentView: 0x117b04c40; frame = (0 0; 0 0); clipsToBounds = YES; opaque = NO; autoresize = W+H; layer = <CALayer: 0x283491be0>>
I am not sure why that is though and I do not know how to debug this since I am not specifying any layout explicitly. From what I understand, when I present this view controller, I should not have to specify any constraints or sizes as that is to be handled by the view controller transition. All I am doing is trying to make a view controller, that was a child view controller, be presented modally full screen.
The view containment calls are incorrect. (See below.)
But the “Unbalanced calls” error message suggests that there might be some other, deeper problem elsewhere in your code base. The incorrect view controller containment calls are insufficient to manifest this error.
One generally gets this error when attempting to initiate a transition while another is underway (e.g., trying to present/dismiss view controllers inside the viewDidLoad, viewWillAppear or viewWillDisappear methods).
But the supplied code snippet is insufficient to manifest the problem you describe. We need MCVE. I would suggest you create a blank project and figure out what you need to add to it in order to manifest your error.
That having been said, the correct view controller containment calls to remove a child are willMoveToParentViewController, followed by removeFromSuperview, followed by removeFromParentViewController, e.g.:
[self willMoveToParentViewController:nil];
[self.view removeFromSuperview];
[self removeFromParentViewController];
Note, I did not call didMoveToParentViewController, because, as the documentation says:
The removeFromParentViewController method automatically calls the didMoveToParentViewController: method of the child view controller after it removes the child.
Obviously, when adding a child, the converse is true, that you do not call willMoveToParentViewController, but you do call didMoveToParentViewController:
[self addChildViewController:child];
[self.view addSubview:child.view];
child.view.frame = ...;
[self didMoveToParentViewController:self];
Again, the documentation advises us:
When your custom container calls the addChildViewController: method, it automatically calls the willMoveToParentViewController: method of the view controller to be added as a child before adding it.
I have my own subclass of UITabBarViewController.
Is it possible to change frame for all embedded viewcontrollers' views so that own UITabBarViewController view will be visible partially?
On the attached image I set purple color for own tabBarController view.
I want to change frame of each selected view controller so that this purple view (UITabBarController view) will be visible.
I stumbled upon this answer looking for a solution myself, and found an okayish way to handle this: wrap your viewController inside another viewController as a childViewController.
Essentially, you would present a viewController with clear background, which has your content controller as childController with a frame you want it to have:
UIViewController *wrapperController = [UIViewController new];
wrapperController.backgroundColor = [UIColor clearColor];
[wrapperController addChildViewController:vc];
[wrapperController.view addSubview:vc.view];
vc.view.frame = CGRectMake(...);
Just make sure to pass the tabbarItem to the wrapper, and use that one instead of the child.
I have been following a solution on this question in order to display a view with a transparent background. The issue that I'm having is once the modal view controller has been displayed, the underlying view doesn't get rotated anymore.
For example if A is my view controller, then B is my modal view. The issue is as follows. I currently have my device in portrait and have A displayed. I then present B modally. I then rotate my device and B rotates with it, however A stays as it was.
Please could someone advise on how to handle this rotation so that the underlying view (A) gets rotated too?
ModalViewController is used to interrupt the current workflow and displaying a new set of views. So when you present modally, here in this case you are presenting B, the current active Viewcontroller is B and not A.
A ViewController is traditional controller objects in the Model-View-Controller (MVC) design pattern. They also take care of user interface, gesture recognitions,event management(of buttons for example) and the alignment of views in present in them.
When you presented B, the current viewcontroller changed from A to B and hence when you try to rotate(if the orientation support is provided) the view of B is effected as its the viewcontroller active and it responds to the rotation. Normally we go unnoticed these because the view is opaque. Here in your case the view is transparent and we notice that A has not responded to rotation.
I tried the above example in iOS6 (from the one you mentioned)
ViewController2 *vc = [self.storyboard instantiateViewControllerWithIdentifier:#"VC2"];
vc.view.backgroundColor = [UIColor clearColor];
self.modalPresentationStyle = UIModalPresentationCurrentContext;
[self presentViewController:vc animated:YES completion:nil];
here A remained in portrait mode
When i did this adding the second viewcontroller's view as a subview, A changed to landscape
ViewController2 *vc = [self.storyboard instantiateViewControllerWithIdentifier:#"VC2"];
vc.view.backgroundColor = [UIColor clearColor];
self.view addSubview:vc.view];
this happend because in the second trial the active viewcontroller was A and not B as B's view was a subview added to A. Go through Apples's Document on
About ViewController
About windows and views
Presenting ViewControllers
I have an UIViewController(called MainViewController) which presents modally a semi-transparent view (HelpOverlayViewController):
HelpOverlayViewController *helpOverlayViewController = [[HelpOverlayViewController alloc] init];
self.modalPresentationStyle = UIModalPresentationCurrentContext;
helpOverlayViewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentViewController:helpOverlayViewController animated:YES completion:nil];
If the user rotates the device while the HelpOverlayViewController is shown it only rotates HelpOverlayViewController and not the MainViewController i.e. the parent controller. This is a problem since HelpOverlayViewController is semi-transparent and MainViewController is visible below it.
Both controllers have the method
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
They both rotate fine independently.
Is there some way I can force the underlaying view controller to rotate when the modal view does?
I do know that issues like this will likely be largely resolved with iOS 6 as it has a different model for handling rotations.
However, that doesn't help you now. You might be best off just making your HelpOverlay a UIView and not a UIViewController. You can add this semi-transparent view onto the top of your MainViewController (or any other). You can still create an animation (like a fade-in) when adding this subview to your view hierarchy. With this model, you'll no longer have any issues with rotations.
I'm trying to make a custom segue so that the destination view controller slides down from the top.
I wrote my code according to the example from the documentation.
The problem is that when the segue is executed, the source view controller goes black, and then the animation occurs. How can I prevent the source view controller from going black?
(I already tried implementing the solution presented in this answer but the screen either goes black after the transition, or reverts to the source view controller.)
Here's my code:
-(void)perform{
UIViewController *splashScreen = self.sourceViewController;
UIViewController *mainScreen = self.destinationViewController;
//Place the destination VC above the visible area
mainScreen.view.center = CGPointMake(mainScreen.view.center.x,
mainScreen.view.center.y-600);
//Animation to move the VC down into the visible area
[UIView animateWithDuration:1
animations:^{
mainScreen.view.center = CGPointMake(mainScreen.view.center.x, [[UIScreen mainScreen] bounds].size.height/2 );
}
];
[splashScreen presentModalViewController:mainScreen animated:NO];
}
The reason that your source view controller seems to be hidden is that the destination view controller is presented right away.
When you are writing custom segues you don't have both views available at the same time. You could either
push view controller, add the source view to the destination view controller and animate
add the destination view to the source view controller and animate, then push view controller
push to an in-between view controller, add both views, animate, push to the destination view controller.
In all the above cases where I say push view controllers you could instead present view controllers modally. In fact that might be more suitable for the in-between view controller solution.