Changing the iOS root view controller

Is the iOS window root view controller usually initialized once at the start by the tab bar controller or navigation controller? Is it possible to change the root view controller in an application several times?

I have a scenario in which the top view is different from the user action. I was thinking about having a navigation controller with a top-level controller having a splash screen image and, if necessary, pressing / displaying the controllers. Alternatively, I can continue to change the window controller. What would be the best approach?

+51
ios
Apr 02 '13 at
source share
5 answers

Commonly used is the "present view controller" ( presentViewController:animated:completion: . You can have as much as you like, effectively appearing in front of (and basically replacing) the root view controller. There doesnโ€™t need to be animation if you donโ€™t want, or maybe. You can reject the presented view controller to return to the original root view controller, but you do not need to; the presented view controller may just be forever if you like.

Here's a section on the view controllers presented from my book:

http://www.apeth.com/iOSBook/ch19.html#_presented_view_controller

In this diagram (earlier in this chapter), the presented view controller has completely switched to the application interface; the root view controller and its children are no longer in the interface. The root view controller still exists, but it is lightweight and does not matter.

enter image description here

+42
Apr 02
source share

iOS 8.0, Xcode 6.0.1, ARC enabled

Most of your questions have been answered. However, I can solve the problem that I recently had to deal with.

Is it possible to change the root view controller several times in an application?

The answer is yes . I had to do this recently in resetting my UIView hierarchy after the initial UIView that were part of the application. launch is no longer needed. In other words, you can reset your "rootViewController" from any other UIViewController anytime after the application. "didFinishLoadingWithOptions".

To do this...

1) Declare a link to your application. delegate (application called "Test") ...

 TestAppDelegate *testAppDelegate = (TestAppDelegate *)[UIApplication sharedApplication].delegate; 

2) Select the UIViewController that you want to make your "rootViewController"; either from the storyboard, or determined programmatically ...

  1. a) storyboard (make sure the identifier, i.e. the storyboardID, exists in the Identity Inspector for the UIViewController):
 UIStoryboard *mainStoryBoard = [UIStoryboard storyboardWithName:@"Main" bundle:nil]; NewRootViewController *newRootViewController = [mainStoryBoard instantiateViewControllerWithIdentifier:@"NewRootViewController"]; 

  1. b) programmatically (possibly addSubview, etc.)
 UIViewController *newRootViewController = [[UIViewController alloc] init]; newRootViewController.view = [[UIView alloc] initWithFrame:CGRectMake(0, 50, 320, 430)]; newRootViewController.view.backgroundColor = [UIColor whiteColor]; 

3) Putting it all together ...

  testAppDelegate.window.rootViewController = newRootViewController; [testAppDelegate.window makeKeyAndVisible]; 

4) You can even add animation ...

 testAppDelegate.window.rootViewController = newRootViewController; [testAppDelegate.window makeKeyAndVisible]; newRootViewController.view.alpha = 0.0; [UIView animateWithDuration:2.0 animations:^{ newRootViewController.view.alpha = 1.0; }]; 

Hope this helps someone! Greetings.

The root controller for the window.

The root view controller provides a view of the contents of the window. Assigning a view controller to this property (either programmatically or using Interface Builder) sets the view controllers to view as window contents. If the window has an existing hierarchy view, the old views are deleted before the new ones are installed. The default value of this property is nil.

* Update 9/2/2015

As the comments below indicate, you should handle the removal of the old view controller when the new view controller is presented. You can choose a transitional view controller in which you will deal with this. Here are some tips on how to implement this:

 [UIView transitionWithView:self.containerView duration:0.50 options:options animations:^{ //Transition of the two views [self.viewController.view removeFromSuperview]; [self.containerView addSubview:aViewController.view]; } completion:^(BOOL finished){ //At completion set the new view controller. self.viewController = aViewController; }]; 
+46
Oct 16 '14 at 11:19
source share

From the comments on serge-k, I created a working solution with a workaround for strange behavior when there is a modal view controller presented on top of the old rootViewController:

 extension UIView { func snapshot() -> UIImage { UIGraphicsBeginImageContextWithOptions(bounds.size, false, UIScreen.mainScreen().scale) drawViewHierarchyInRect(bounds, afterScreenUpdates: true) let result = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return result } } extension UIWindow { func replaceRootViewControllerWith(_ replacementController: UIViewController, animated: Bool, completion: (() -> Void)?) { let snapshotImageView = UIImageView(image: self.snapshot()) self.addSubview(snapshotImageView) let dismissCompletion = { () -> Void in // dismiss all modal view controllers self.rootViewController = replacementController self.bringSubview(toFront: snapshotImageView) if animated { UIView.animate(withDuration: 0.4, animations: { () -> Void in snapshotImageView.alpha = 0 }, completion: { (success) -> Void in snapshotImageView.removeFromSuperview() completion?() }) } else { snapshotImageView.removeFromSuperview() completion?() } } if self.rootViewController!.presentedViewController != nil { self.rootViewController!.dismiss(animated: false, completion: dismissCompletion) } else { dismissCompletion() } } } 

To replace rootViewController just use:

 let newRootViewController = self.storyboard!.instantiateViewControllerWithIdentifier("BlackViewController") UIApplication.sharedApplication().keyWindow!.replaceRootViewControllerWith(newRootViewController, animated: true, completion: nil) 

Hope this helps :) tested on iOS 8.4; also tested for support of navigation controllers (should also support control panel controllers, etc., but I have not tested it)

Explanation

If there is a modal view controller presented on top of the old rootViewController, the rootViewController is replaced, but the old view still hangs under the new rootViewController (and can be seen, for example, during Flip Horizontal or Cross Dissolve transition animations) and the old view controller hierarchy remains highlighted (which can cause serious memory problems if the replacement occurs several times).

Thus, the only solution is to discard all modal view controllers, and then replace the rootViewController. A screenshot is placed above the window during dismissal and replacement to hide the ugly flashing process.

+18
Jan 08 '16 at 14:37
source share

You can change the rootViewController window throughout the application life cycle.

 UIViewController *viewController = [UIViewController alloc] init]; [self.window setRootViewController:viewController]; 

When you change the rootViewController, you can still add the UIImageView as a subview in the window to act like a splash. Hope this makes sense, something like this:

 - (void) addSplash { CGRect rect = [UIScreen mainScreen].bounds; UIImageView *splashImage = [[UIImageView alloc] initWithFrame:rect]; splashImage.image = [UIImage imageNamed:@"splash.png"]; [self.window addSubview:splashImage]; } - (void) removeSplash { for (UIView *view in self.window.subviews) { if ([view isKindOfClass:[UIImageView class]]) { [view removeFromSuperview]; } } } 
+5
Apr 02
source share

For iOS8, we also need to set below two parameters in YES.

 providesPresentationContextTransitionStyle definesPresentationContext 

Here is my code to represent the transparent model controller in the navigation controller for iOS 6 and above.

 ViewController *vcObj = [[ViewController alloc] initWithNibName:NSStringFromClass([ViewController class]) bundle:nil]; UINavigationController *navCon = [[UINavigationController alloc] initWithRootViewController:vcObj]; if ([[UIDevice currentDevice].systemVersion floatValue] >= 8.0) { navCon.providesPresentationContextTransitionStyle = YES; navCon.definesPresentationContext = YES; navCon.modalPresentationStyle = UIModalPresentationOverCurrentContext; [self presentViewController:navCon animated:NO completion:nil]; } else { AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate]; [self presentViewController:navCon animated:NO completion:^{ [navCon dismissViewControllerAnimated:NO completion:^{ appDelegate.window.rootViewController.modalPresentationStyle = UIModalPresentationCurrentContext; [self presentViewController:navCon animated:NO completion:nil]; appDelegate.window.rootViewController.modalPresentationStyle = UIModalPresentationFullScreen; }]; }]; } 
+3
Dec 07 '14 at 4:21
source share



All Articles