RootViewController Switch Transition Animation

¿Hay alguna forma de tener un efecto de transición / animación al reemplazar un controlador de vista existente como rootviewcontroller por uno nuevo en la aplicaciónDelegate?

Puede ajustar la conmutación de rootViewController en un bloque de animación de transición:

 [UIView transitionWithView:self.window duration:0.5 options:UIViewAnimationOptionTransitionFlipFromLeft animations:^{ self.window.rootViewController = newViewController; } completion:nil]; 

Encontré esto y funciona perfectamente:

en su appDelegate:

 - (void)changeRootViewController:(UIViewController*)viewController { if (!self.window.rootViewController) { self.window.rootViewController = viewController; return; } UIView *snapShot = [self.window snapshotViewAfterScreenUpdates:YES]; [viewController.view addSubview:snapShot]; self.window.rootViewController = viewController; [UIView animateWithDuration:0.5 animations:^{ snapShot.layer.opacity = 0; snapShot.layer.transform = CATransform3DMakeScale(1.5, 1.5, 1.5); } completion:^(BOOL finished) { [snapShot removeFromSuperview]; }]; } 

en tu aplicación

  if (!app) { app = (AppDelegate *)[[UIApplication sharedApplication] delegate]; } [app changeRootViewController:newViewController]; 

créditos:

https://gist.github.com/gimenete/53704124583b5df3b407

Estoy publicando la respuesta de Jesús implementada en breve. Toma el identificador de viewcontroller como argumento, carga desde el guión gráfico deseado ViewController y cambia a rootViewController con animación.

Actualización de Swift 3.0:

  func changeRootViewController(with identifier:String!) { let storyboard = self.window?.rootViewController?.storyboard let desiredViewController = storyboard?.instantiateViewController(withIdentifier: identifier); let snapshot:UIView = (self.window?.snapshotView(afterScreenUpdates: true))! desiredViewController?.view.addSubview(snapshot); self.window?.rootViewController = desiredViewController; UIView.animate(withDuration: 0.3, animations: {() in snapshot.layer.opacity = 0; snapshot.layer.transform = CATransform3DMakeScale(1.5, 1.5, 1.5); }, completion: { (value: Bool) in snapshot.removeFromSuperview(); }); } 

Actualización de Swift 2.2:

  func changeRootViewControllerWithIdentifier(identifier:String!) { let storyboard = self.window?.rootViewController?.storyboard let desiredViewController = storyboard?.instantiateViewControllerWithIdentifier(identifier); let snapshot:UIView = (self.window?.snapshotViewAfterScreenUpdates(true))! desiredViewController?.view.addSubview(snapshot); self.window?.rootViewController = desiredViewController; UIView.animateWithDuration(0.3, animations: {() in snapshot.layer.opacity = 0; snapshot.layer.transform = CATransform3DMakeScale(1.5, 1.5, 1.5); }, completion: { (value: Bool) in snapshot.removeFromSuperview(); }); } class func sharedAppDelegate() -> AppDelegate? { return UIApplication.sharedApplication().delegate as? AppDelegate; } 

Después, tienes un uso muy simple desde cualquier lugar:

 let appDelegate = AppDelegate.sharedAppDelegate() appDelegate?.changeRootViewControllerWithIdentifier("YourViewControllerID") 

Actualización de Swift 3.0

 let appDelegate = UIApplication.shared.delegate as! AppDelegate appDelegate.changeRootViewController(with: "listenViewController") 

// En swift:

 UIView.transitionWithView(self.window!, duration: 0.5, options: UIViewAnimationOptions.TransitionFlipFromLeft, animations: { self.window?.rootViewController = anyViewController }, completion: nil) 

solo prueba esto Funciona bien para mí

 BOOL oldState = [UIView areAnimationsEnabled]; [UIView setAnimationsEnabled:NO]; self.window.rootViewController = viewController; [UIView transitionWithView:self.window duration:0.5 options:transition animations:^{ // } completion:^(BOOL finished) { [UIView setAnimationsEnabled:oldState]; }]; 

EDITAR:

Éste es mejor.

 - (void)setRootViewController:(UIViewController *)viewController withTransition:(UIViewAnimationOptions)transition completion:(void (^)(BOOL finished))completion { UIViewController *oldViewController = self.window.rootViewController; [UIView transitionFromView:oldViewController.view toView:viewController.view duration:0.5f options:(UIViewAnimationOptions)(transition|UIViewAnimationOptionAllowAnimatedContent|UIViewAnimationOptionLayoutSubviews) completion:^(BOOL finished) { self.window.rootViewController = viewController; if (completion) { completion(finished); } }]; } 

Para no tener problemas con el cambio de transición más adelante en la aplicación, es bueno borrar también la vista anterior de la stack

 UIViewController *oldController=self.window.rootViewController; [UIView transitionWithView:self.window duration:0.5 options:UIViewAnimationOptionTransitionCrossDissolve animations:^{ self.window.rootViewController = nav; } completion:^(BOOL finished) { if(oldController!=nil) [oldController.view removeFromSuperview]; }]; 

La respuesta correcta es que no necesita reemplazar el rootViewController en su ventana. En su lugar, cree un UIViewController personalizado, UIViewController una vez y deje que muestre un controlador secundario a la vez y reemplácelo con animación si es necesario. Puede usar el siguiente fragmento de código como punto de partida:

Swift 3.0

 import Foundation import UIKit /// Displays a single child controller at a time. /// Replaces the current child controller optionally with animation. class FrameViewController: UIViewController { private(set) var displayedViewController: UIViewController? func display(_ viewController: UIViewController, animated: Bool = false) { addChildViewController(viewController) let oldViewController = displayedViewController view.addSubview(viewController.view) viewController.view.layoutIfNeeded() let finishDisplay: (Bool) -> Void = { [weak self] finished in if !finished { return } oldViewController?.view.removeFromSuperview() oldViewController?.removeFromParentViewController() viewController.didMove(toParentViewController: self) } if (animated) { viewController.view.alpha = 0 UIView.animate( withDuration: 0.5, animations: { viewController.view.alpha = 1; oldViewController?.view.alpha = 0 }, completion: finishDisplay ) } else { finishDisplay(true) } displayedViewController = viewController } override var preferredStatusBarStyle: UIStatusBarStyle { return displayedViewController?.preferredStatusBarStyle ?? .default } } 

Y la forma en que lo usa es:

 ... let rootController = FrameViewController() rootController.display(UINavigationController(rootViewController: MyController())) window.rootViewController = rootController window.makeKeyAndVisible() ... 

El ejemplo anterior demuestra que puedes anidar UINavigationController dentro de FrameViewController y que funciona bien. Este enfoque le proporciona un alto nivel de personalización y control. Simplemente llame a FrameViewController.display(_) cualquier momento que desee reemplazar el controlador raíz en su ventana, y lo hará por usted.

Esta es una actualización para swift 3, este método debe estar en su delegado de aplicación, y lo llama desde cualquier controlador de vista, a través de una instancia compartida del delegado de la aplicación

 func logOutAnimation() { let storyBoard = UIStoryboard.init(name: "SignIn", bundle: nil) let viewController = storyBoard.instantiateViewController(withIdentifier: "signInVC") UIView.transition(with: self.window!, duration: 0.5, options: UIViewAnimationOptions.transitionFlipFromLeft, animations: { self.window?.rootViewController = viewController self.window?.makeKeyAndVisible() }, completion: nil) } 

La parte que falta de varias preguntas arriba, es

  self.window?.makeKeyAndVisible() 

Espero que esto ayude a alguien.

en AppDelegate.h:

 #define ApplicationDelegate ((AppDelegate *)[UIApplication sharedApplication].delegate)] 

en tu controlador:

 [UIView transitionWithView:self.window duration:0.5 options:UIViewAnimationOptionTransitionFlipFromLeft animations:^{ ApplicationDelegate.window.rootViewController = newViewController; } completion:nil]; 

Propongo mi camino, que está funcionando bien en mi proyecto, y me ofrece buenas animaciones. He probado otras propuestas que se encuentran en esta publicación, pero algunas de ellas no funcionan como se esperaba.

 - (void)transitionToViewController:(UIViewController *)viewController withTransition:(UIViewAnimationOptions)transition completion:(void (^)(BOOL finished))completion { // Reset new RootViewController to be sure that it have not presented any controllers [viewController dismissViewControllerAnimated:NO completion:nil]; [UIView transitionWithView:self.window duration:0.5f options:transition animations:^{ for (UIView *view in self.window.subviews) { [view removeFromSuperview]; } [self.window addSubview:viewController.view]; self.window.rootViewController = viewController; } completion:completion]; } 

Bonita y dulce animación (probada con Swift 4.x):

 extension AppDelegate { public func present(viewController: UIViewController) { guard let window = window else { return } UIView.transition(with: window, duration: 0.5, options: .transitionFlipFromLeft, animations: { window.rootViewController = viewController }, completion: nil) } } 

Llamar con

 guard let delegate = UIApplication.shared.delegate as? AppDelegate else { return } delegate.present(viewController: UIViewController())