Is it possible for a UIViewController to present i

2019-07-07 01:50发布

I have a UIViewController (lets call it Fred) which may be embedded inside many different types of containers (UINavigationController, UITabBarController, or within a modal). Fred has a button that when tapped, needs to make Fred fullscreen.

Intuitively, this means I would like to present Fred as a fullscreen modal, but there are two problems:

  1. Fred is active in the view hierarchy (cannot present a vc that is active)
  2. Fred would need to present himself?

I tried to solve this by creating a fullscreenContainerViewController which Fred can call like this:

[fullscreenContainer presentViewControllerAsFullscreen:self]

Implementation of FullscreenContainerViewController:

@property (nonatomic) UIViewController* proxyViewController

// Create a proxy VC to hold state of the original ViewController so that we can safely
// Remove it from it's hierarchy and then restore it after fullscreen is dismissed.
 - (void)presentViewControllerAsFullscreen:(UIViewController*)originalController
{        
    UIViewController* originalParentViewController = originalController.parentViewController;
    UIView* originalSuperview = originalController.view.superview;

    self.proxyViewController = [[UIViewController alloc] init];
    self.proxyViewController.view = [[UIView alloc] initWithFrame:originalController.view.frame];
    self.proxyViewController.view.autoresizingMask = originalController.view.autoresizingMask;

    // Detach contentController from parent
    [originalController willMoveToParentViewController:nil];
    [originalController.view removeFromSuperview];
    [originalController removeFromParentViewController];

    // Put in the proxy view controller in place of the orignalController
    [originalParentViewController addChildViewController:self.proxyViewController];
    [originalSuperview addSubview:self.proxyViewController.view];
    [self.proxyViewController didMoveToParentViewController:originalParentViewController];
    originalController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
    [self.proxyViewController presentViewController:originalController animated:YES completion:nil];
}

And then to dismiss:

- (void)dismissFullscreenWithCompletionHandler:(void (^)(void))completionHandler
{
    UIViewController* parentToRestoreTo = self.proxyViewController.parentViewController;
    UIView* superviewToRestoreTo = self.proxyViewController.view.superview;

    [self.proxyViewController dismissViewControllerAnimated:YES completion:^(void) {

        // Detach proxy view/controller
        [self.proxyViewController removeFromParentViewController];
        [self.proxyViewController.view removeFromSuperview];
        self.proxyViewController = nil;

        // Restore original parent relationship
        [parentToRestoreTo addChildViewController:self.contentController];
        [superviewToRestoreTo addSubview:self.contentController.view];
        [self.contentController didMoveToParentViewController:parentToRestoreTo];
        if (self.shouldRestoreStatusBar) {
            [[UIApplication sharedApplication] setStatusBarHidden:NO                       
                                                    withAnimation:UIStatusBarAnimationSlide];
        }
    }];
}

This works, but it seems anti pattern possibly? Maybe it will break in some subtle way? Is there a better approach to what I'm trying to accomplish?

0条回答
登录 后发表回答