¿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:
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())