ios - life - UIViewController-problema con la transición de despido personalizado
viewcontroller swift (2)
Resumen
Tengo un contenido UIViewController que presenta una configuración UIViewController utilizando una transición personalizada. La presentación es con presentViewController:animated:completion:
Cuando más tarde dismissViewControllerAnimated:completion:
la configuración con dismissViewControllerAnimated:completion:
el controlador de presentación vuelve repentinamente a su posición inicial antes de la presentación del controlador de configuración.
Tengo una solución para esto en el dispositivo pero no en el simulador. Sin embargo, me gustaría saber qué estoy haciendo mal en lugar de piratear un cuerpo que lo hace desaparecer. También planeo hacer esta animación interactiva, y sospecho que estos problemas se ampliarán cuando lo haga.
Transición personalizada - Abrir el capó
El efecto deseado es que el controlador de presentación se desliza hacia abajo de la pantalla, y se ve que el controlador presentado se encuentra detrás de él desde donde se levanta para llenar la pantalla. La parte superior del controlador de presentación permanece en pantalla durante la vida útil de uso del controlador presentado. Permanece en la parte inferior de la pantalla, pero por encima del controlador presentado.
Se podría imaginar levantando el capó en un automóvil (el controlador de presentación frontal) para ver el motor detrás (los ajustes presentados), pero el capó permanece visible en la parte inferior para un poco de contexto.
Planeo refinar esto para que el controlador de presentación parezca realmente mejorar la perspectiva de una manera tridimensional, pero aún no he llegado tan lejos.
Cuando se descartan los ajustes, el controlador de presentación original (capó) debe deslizarse hacia atrás en la pantalla y el controlador presentado (ajustes) debe retroceder ligeramente (cerrando el capó).
Código
Este es el método que alterna la configuración dentro y fuera de la pantalla (solo es llamado por un UIButton). Notará que el controlador de vista de presentación se configura como el <UIViewControllerTransitioningDelegate>
.
-(void) toggleSettingsViewController
{
const BOOL settingsAreShowing = [self presentedViewController] != nil;
if(!settingsAreShowing)
{
UIViewController *const settingsController = [[self storyboard] instantiateViewControllerWithIdentifier: @"STSettingsViewController"];
[settingsController setTransitioningDelegate: self];
[settingsController setModalPresentationStyle: UIModalPresentationCustom];
[self presentViewController: settingsController animated: YES completion: nil];
}
else
{
[self dismissViewControllerAnimated: YES completion: nil];
}
}
Para implementar <UIViewControllerAnimatedTransitioning>
el controlador de la vista de presentación también se devuelve solo como <UIViewControllerAnimatedTransitioning>
-(id<UIViewControllerAnimatedTransitioning>) animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source
{
return self;
}
-(id<UIViewControllerAnimatedTransitioning>) animationControllerForDismissedController:(UIViewController *)dismissed
{
// Test Point 1.
return self;
}
Así que finalmente, el controlador de vista de presentación recibirá animateTransition:
:
-(void) animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
UIViewController *const fromController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *const toController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
const BOOL isUnwinding = [toController presentedViewController] == fromController;
const BOOL isPresenting = !isUnwinding;
UIViewController * presentingController = isPresenting ? fromController : toController;
UIViewController * presentedController = isPresenting ? toController : fromController;
if(isPresenting)
{
// Add the presented controller (settings) to the view hierarchy _behind_ the presenting controller.
[[transitionContext containerView] insertSubview: [presentedController view] belowSubview: [presentingController view]];
// Set up the initial position of the presented settings controller. Scale it down so it seems in the distance. Alpha it down so it is dark and shadowed.
presentedController.view.transform = CGAffineTransformMakeScale(0.9, 0.9);
presentedController.view.alpha = 0.7;
[UIView animateWithDuration: [self transitionDuration: transitionContext] animations:^{
// Lift up the presented controller.
presentedController.view.transform = CGAffineTransformMakeScale(1.0, 1.0);
// Brighten the presented controller (out of shadow).
presentedController.view.alpha = 1;
// Push the presenting controller down the screen – 3d effect to be added later.
presentingController.view.layer.transform = CATransform3DMakeTranslation(0,400,0);
} completion: ^(BOOL finished){
[transitionContext completeTransition: ![transitionContext transitionWasCancelled]];
}];
}
else
{
// Test Point 2.
// !!!This line should not be needed!!!
// It resets the presenting controller to where it ought to be anyway.
presentingController.view.layer.transform = CATransform3DMakeTranslation(0,400,0);
[UIView animateWithDuration: [self transitionDuration: transitionContext] animations:^{
// Bring the presenting controller back to its original position.
presentingController.view.layer.transform = CATransform3DIdentity;
// Lower the presented controller again and put it back in to shade.
presentedController.view.transform = CGAffineTransformMakeScale(0.9, 0.9);
presentedController.view.alpha = 0.4;
} completion:^(BOOL finished) {
[transitionContext completeTransition: ![transitionContext transitionWasCancelled]];
}];
}
}
-(NSTimeInterval) transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext
{
return 0.5;
}
Problema
En el código de arriba, he indicado que esta línea no debería ser necesaria. .
Lo que sucede es que entre el Punto de prueba 1 y el Punto de prueba 2, la posición de la pantalla del controlador de vista de presentación se restablece para que sea el límite predeterminado de la pantalla completa. Así que, en lugar de estar en la parte inferior de la pantalla listo para animar la copia de seguridad nuevamente, súbitamente salta de la pantalla a la posición en la que está destinado a animar suavemente también.
He probado varios enfoques para animar el controlador de vista de presentación en la pantalla:
- He cambiado el marco de su vista.
- He cambiado la transformación de su vista.
- He cambiado la transformación 3d de la capa de su vista.
En todos los casos, en el Punto de prueba 1 , cuando se solicita al delegado de transición, el controlador de presentación se configura como esperaría. Sin embargo, en todos los casos, en el Punto de prueba 2 , el controlador de vista de presentación ha perdido la posición correcta y se ha "borrado" para tener la posición normal de pantalla completa en la que quiero animarlo.
En el trabajo alrededor de arriba, reubico explícitamente el controlador de la vista de presentación al lugar donde debería estar al comienzo de la animación con !!! Esta línea no debería ser necesaria. . Esto parece funcionar en el dispositivo con la versión actual de iOS 7. Sin embargo, en el simulador, el controlador es visible en la posición despejada para al menos un cuadro.
Sospecho que estoy haciendo otra cosa mal, y que voy a meterme en problemas con mi solución simplemente para ocultar otro problema.
¿Alguna idea de qué está pasando? ¡Gracias!
Algunos posibles errores con la eliminación de los controladores de vista presentados de manera modesta utilizando animaciones de transición personalizadas:
- Agregue la vista presentada ("a") al contenedor, luego ponga la vista presentada al frente. No agregue la vista de presentación, ya que podría eliminarla de su supervisión actual.
- Al descartar, UIKit establece el alfa de la vista presentada en 0 antes de llamar a animateTransition . Así que querrá configurarlo en 1.0 o lo que fuera al final del presente antes de disparar su (s) animación (s) de despedida.
- Igualmente para la transformación de la vista presentada. Al descartarse, se restablece la identidad antes de llamar a su animateTransition.
Dado todo eso, creo que esto debería funcionar:
-(void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
UIViewController *fromController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIView *containerView = transitionContext.containerView;
const BOOL isUnwinding = [toController presentedViewController] == fromController;
const BOOL isPresenting = !isUnwinding;
UIViewController *presentingController = isPresenting ? fromController : toController;
UIViewController *presentedController = isPresenting ? toController : fromController;
[containerView addSubview:presentingController.view];
[containerView bringSubviewToFront:presentingController.view];
if(isPresenting)
{
// Set up the initial position of the presented settings controller. Scale it down so it seems in the distance. Alpha it down so it is dark and shadowed.
presentedController.view.transform = CGAffineTransformMakeScale(0.9, 0.9);
presentedController.view.alpha = 0.7;
[UIView animateWithDuration: [self transitionDuration: transitionContext] animations:^{
// Lift up the presented controller.
presentedController.view.transform = CGAffineTransformMakeScale(1.0, 1.0);
// Brighten the presented controller (out of shadow).
presentedController.view.alpha = 1;
// Push the presenting controller down the screen – 3d effect to be added later.
presentingController.view.layer.transform = CATransform3DMakeTranslation(0,400,0);
} completion: ^(BOOL finished){
[transitionContext completeTransition: ![transitionContext transitionWasCancelled]];
}];
}
else
{
presentedController.view.transform = CGAffineTransformMakeScale(0.9, 0.9);
presentedController.view.alpha = 0.7;
[UIView animateWithDuration: [self transitionDuration: transitionContext] animations:^{
// Bring the presenting controller back to its original position.
presentingController.view.layer.transform = CATransform3DIdentity;
// Lower the presented controller again and put it back in to shade.
presentedController.view.transform = CGAffineTransformMakeScale(0.9, 0.9);
presentedController.view.alpha = 0.4;
} completion:^(BOOL finished) {
[transitionContext completeTransition: ![transitionContext transitionWasCancelled]];
}];
}
}
Inicialmente, pensé en usar CATransition
para tener un efecto de transición personalizado cuando presentViewController:animated:completion:
and dismissViewControllerAnimated:completion:
a View Controller. Pero desea mostrar una parte de View Controller cuando se presenta la configuración de View Controller, entonces creo que CATransition
no ayudaría porque no tiene control total de lo lejos que quiere mover el View Controller.
Creo que la forma más fácil es tener un solo Controlador de Vista con dos UIView de pantalla completa. Para la primera UIView (vista del Controlador de vista, es decir, vista propia), usted diseña la configuración, y en la segunda UIView, es la Vista normal. En ViewDidLoad, agrega la segunda vista usando [self.view addSubview:2ndView];
. Más adelante, cuando desee presentar la vista de configuración, puede hacer
CGRect frame = secondView.frame;
frame.origin.y = the_y_coordinate_you_like;
UIView animateWithDuration:0.2 animations:^{
secondView.frame = frame;
}];
a continuación, hacer la otra manera para traer de vuelta el 2ndView.