Search code examples
iosuiviewcontrolleruipageviewcontrollerpresentviewcontroller

Present child view controller


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.


Solution

  • 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.