objective c - español - Intercambio de rootViewController con animación
viewwillappear (6)
En lugar de intercambiar rootViewControllers, ¿por qué no creas una vista personalizada, o tal vez solo un UINavigationController (no muestres la barra de título) y colocas las "vistas de raíz" de las que quieres intercambiar debajo de eso?
Tengo problemas para intercambiar rootViewControllers con animación. Aquí está el código que estoy usando:
[UIView transitionWithView:self.window duration:0.8 options:UIViewAnimationOptionTransitionFlipFromRight animations:^{
self.window.rootViewController = self.navigationController;
} completion:nil];
Funciona, excepto que justo antes de la animación, la pantalla se vuelve negra y luego se produce la animación. Parece que el rootViewController original se elimina justo antes de la animación. ¿Algunas ideas?
He encontrado transitionWithView:duration:options:animations:completion: para producir un resultado más confiable.
[UIView transitionWithView:window
duration:0.3
options:UIViewAnimationOptionTransitionFlipFromLeft
animations:^{
[fromView removeFromSuperview];
[window addSubview:toView];
window.rootViewController = toViewController;
}
completion:NULL];
Si lo deja hasta el bloque de finalización para configurar el controlador de la vista raíz, entonces, durante los métodos como ver (Will / Did) aparece
self.view.window.rootViewController
Seguirá configurado en el controlador de vista anterior. Esto puede no ser un problema en la mayoría de las situaciones a menos que necesite pasar esa referencia a rootViewController a otro código durante esos métodos como yo lo hice.
La respuesta actualmente aceptada (en el momento de escribir esta respuesta) no es ideal porque podría no colocar elementos de IU como la barra de navegación correctamente durante la animación o dar lugar a otras fallas de diseño como se describe en la respuesta de Marko Nikolovski . Sin embargo, presionar una instantánea de la vista del controlador de vista actual encima de la vista del controlador de vista siguiente para fines de animación tampoco es ideal porque no se puede usar para controladores de vista en los que cualquier tipo de animación ocurre durante la transición a la siguiente vista controlador como una instantánea es solo una imagen estática .
Tomando la idea básica de cclogg aquí está mi implementación para una transición fluida entre dos controladores de vista que no se preocupen por los valores alfa:
/// Replaces the window''s current `rootViewController` with the `newViewController`
/// passed as a parameter. If `animated`, the window animates the replacement
/// with a cross dissolve transition.
///
/// - Parameters:
/// - newViewController: The view controller that replaces the current view controller.
/// - animated: Specifies if the transition between the two view controllers is animated.
/// - duration: The transition''s duration. (Ignored if `animated == false`)
private func swapCurrentViewController(with newViewController: UIViewController,
animated: Bool = true,
duration: TimeInterval = 1) {
// Get a reference to the window''s current `rootViewController` (the "old" one)
let oldViewController = window?.rootViewController
// Replace the window''s `rootViewController` with the new one
window?.rootViewController = newViewController
if animated, let oldView = oldViewController?.view {
// Add the old view controller''s view on top of the new `rootViewController`
newViewController.view.addSubview(oldView)
// Remove the old view controller''s view in an animated fashion
UIView.transition(with: window!,
duration: duration,
options: .transitionCrossDissolve,
animations: { oldView.removeFromSuperview() },
completion: nil)
}
}
Es importante reemplazar el rootViewController
de vista rootViewController
la ventana antes de iniciar la transición o la animación porque así es como el nuevo controlador de vista llega a conocer su contexto, es decir, los márgenes de diseño correctos, etc.
💡 Este es un camino a seguir si realmente necesita reemplazar el rootViewController
la ventana por cualquier razón. Sin embargo, quiero señalar que desde un punto de vista arquitectónico, un enfoque mucho mejor en mi opinión es crear un controlador de vista que sea el único responsable de manejar las transiciones entre los otros dos controladores de vista y configurarlo como el rootViewController
la ventana y luego nunca cambiarlo Está bien explicado en esta respuesta a una pregunta similar.
La solución que funcionó para mí fue una ligera modificación de la respuesta de Marko Nikolovski. Mi controlador de vista raíz existente tenía una rueda giratoria animada, por lo que una instantánea se veía extraña porque congeló la animación. Al final, pude hacer lo siguiente (dentro del controlador de vista raíz actual):
NextRootViewController *nextRootVC = [self.storyboard instantiateViewControllerWithIdentifier:@"NextRootViewController"];
self.view.window.rootViewController = nextRootVC;
[nextRootVC addSubview:self.view];
[UIView animateWithDuration:0.4 delay:0.2 options:UIViewAnimationOptionTransitionCrossDissolve animations:^{
self.view.alpha = 0;
} completion:^(BOOL finished) {
[self.view removeFromSuperview];
}];
Soy consciente de que esta es una pregunta bastante antigua, sin embargo, ni la respuesta aceptada ni las alternativas proporcionaron una buena solución (cuando la animación terminó la barra de navegación del nuevo controlador de vista raíz parpadeó desde el blanco al color de fondo, lo cual es muy discordante) .
Lo arreglé haciendo una instantánea de la última pantalla del primer controlador de vista, superponiéndolo sobre el segundo controlador de vista (destino) y luego animándolo como quería después de establecer el segundo controlador de vista como raíz:
UIView *overlayView = [[UIScreen mainScreen] snapshotViewAfterScreenUpdates:NO];
[self.destinationViewController.view addSubview:overlayView];
self.window.rootViewController = self.destinationViewController;
[UIView animateWithDuration:0.4f delay:0.0f options:UIViewAnimationOptionTransitionCrossDissolve animations:^{
overlayView.alpha = 0;
} completion:^(BOOL finished) {
[overlayView removeFromSuperview];
}];
EDITAR: versión Swift 3 del código:
let overlayView = UIScreen.main.snapshotView(afterScreenUpdates: false)
destinationViewController.view.addSubview(overlayView)
window.rootViewController = destinationViewController
UIView.animate(withDuration: 0.4, delay: 0, options: .transitionCrossDissolve, animations: {
overlayView.alpha = 0
}, completion: { finished in
overlayView.removeFromSuperview()
})
transitionWithView
está destinado a animar subvistas de la vista de contenedor especificada. No es tan simple animar cambiando el controlador de vista raíz. He pasado mucho tiempo tratando de hacerlo sin efectos secundarios. Ver:
EDITAR: fragmento añadido de la respuesta a la que se hace referencia
[UIView transitionFromView:self.window.rootViewController.view
toView:viewController.view
duration:0.65f
options:transition
completion:^(BOOL finished){
self.window.rootViewController = viewController;
}];