ios - sola - ¿Cómo puedo implementar una animación de deslizar/deslizar entre vistas?
quitar animaciones ios 11 (3)
Tengo algunas vistas entre las que quiero deslizar en un programa de iOS. En este momento, estoy deslizando entre ellos usando un estilo modal, con una animación de disolución cruzada. Sin embargo, quiero tener una animación de deslizar / deslizar como se ve en la pantalla de inicio y tal. No tengo idea de cómo codificar esa transición, y el estilo de animación no es un estilo de transición modal disponible. ¿Alguien puede darme un ejemplo del código? No necesita ser un modelo modal ni nada, simplemente lo encontré más fácil.
Desde iOS 7, si desea animar la transición entre dos controladores de vista, usaría transiciones personalizadas, como se explica en el video de la WWDC 2013 Transiciones personalizadas usando controladores de vista . Por ejemplo, para personalizar la presentación de un nuevo controlador de vista debería:
El controlador de vista de destino especificaría
self.modalPresentationStyle
ytransitioningDelegate
para la animación de presentación:- (instancetype)initWithCoder:(NSCoder *)coder { self = [super initWithCoder:coder]; if (self) { self.modalPresentationStyle = UIModalPresentationCustom; self.transitioningDelegate = self; } return self; }
Este delegado (en este ejemplo, el controlador de vista en sí) se ajustaría a
UIViewControllerTransitioningDelegate
e implementaría:- (id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source { return [[PresentAnimator alloc] init]; } // in iOS 8 and later, you''d also specify a presentation controller - (UIPresentationController *)presentationControllerForPresentedViewController:(UIViewController *)presented presentingViewController:(UIViewController *)presenting sourceViewController:(UIViewController *)source { return [[PresentationController alloc] initWithPresentedViewController:presented presentingViewController:presenting]; }
Implementarías un animador que realizaría la animación deseada:
@interface PresentAnimator : NSObject <UIViewControllerAnimatedTransitioning> @end @implementation PresentAnimator - (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext { return 0.5; } // do whatever animation you want below - (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext { UIViewController* toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey]; UIViewController* fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey]; [[transitionContext containerView] addSubview:toViewController.view]; CGFloat width = fromViewController.view.frame.size.width; CGRect originalFrame = fromViewController.view.frame; CGRect rightFrame = originalFrame; rightFrame.origin.x += width; CGRect leftFrame = originalFrame; leftFrame.origin.x -= width / 2.0; toViewController.view.frame = rightFrame; toViewController.view.layer.shadowColor = [[UIColor blackColor] CGColor]; toViewController.view.layer.shadowRadius = 10.0; toViewController.view.layer.shadowOpacity = 0.5; [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{ fromViewController.view.frame = leftFrame; toViewController.view.frame = originalFrame; toViewController.view.layer.shadowOpacity = 0.5; } completion:^(BOOL finished) { [transitionContext completeTransition:![transitionContext transitionWasCancelled]]; }]; } @end
También implementaría un controlador de presentación que se encargue de limpiar la jerarquía de vistas por usted. En este caso, ya que estamos superponiendo completamente la vista de presentación, podemos eliminarla de la jerarquía cuando se realiza la transición:
@interface PresentationController: UIPresentationController @end @implementation PresentationController - (BOOL)shouldRemovePresentersView { return true; } @end
Opcionalmente, si quieres que este gesto sea interactivo, también deberías:
Cree un controlador de interacción (normalmente, una
UIPercentDrivenInteractiveTransition
);Haga que su
UIViewControllerAnimatedTransitioning
también implemente lainteractionControllerForPresentation
, que obviamente devolvería el controlador de interacción mencionado anteriormente;Tener un gesto (o lo que sea) que actualice la
interactionController
Todo esto se describe en las Transiciones personalizadas antes mencionadas utilizando controladores de vista .
Por ejemplo, para personalizar el push / pop del controlador de navegación , consulte Animación de transición personalizada del controlador de navegación
A continuación, encontrará una copia de mi respuesta original, que precede a las transiciones personalizadas.
La respuesta de @ sooper es correcta, que la transición CAT puede producir el efecto que está buscando.
Pero, por cierto, si su fondo no es blanco, el kCATransitionPush
de CATransition
tiene una kCATransitionPush
CATransition
extrañas al final de la transición que pueden distraer (al navegar entre imágenes, especialmente, le da un ligero parpadeo efecto). Si sufres de esto, encontré que esta transición simple es muy elegante: puedes preparar tu "próxima vista" para que esté justo fuera de la pantalla a la derecha, y luego animar el movimiento de la vista actual fuera de la pantalla a la izquierda mientras simultáneamente animar la vista siguiente para moverse a donde estaba la vista actual. Tenga en cuenta que, en mis ejemplos, estoy animando subvistas dentro y fuera de la vista principal dentro de un solo controlador de vista, pero probablemente tenga una idea:
float width = self.view.frame.size.width;
float height = self.view.frame.size.height;
// my nextView hasn''t been added to the main view yet, so set the frame to be off-screen
[nextView setFrame:CGRectMake(width, 0.0, width, height)];
// then add it to the main view
[self.view addSubview:nextView];
// now animate moving the current view off to the left while the next view is moved into place
[UIView animateWithDuration:0.33f
delay:0.0f
options:UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionAllowUserInteraction
animations:^{
[nextView setFrame:currView.frame];
[currView setFrame:CGRectMake(-width, 0.0, width, height)];
}
completion:^(BOOL finished){
// do whatever post processing you want (such as resetting what is "current" and what is "next")
}];
Claramente, tendrías que modificar esto para que tengas todos tus controles configurados, pero esto produce una transición muy simple, sin desvanecimiento o algo así, solo una transición suave y agradable.
Una advertencia: primero, ni este ejemplo, ni el ejemplo de CATransition
, son como la animación de la pantalla de inicio de SpringBoard (de la que hablaste), que es continua (es decir, si estás a mitad de camino, puedes detenerte y regresar) o lo que sea). Estas transiciones son las que una vez para iniciarlas, simplemente suceden. Si necesita esa interacción en tiempo real, también se puede hacer, pero es diferente.
Actualizar:
Si desea usar un gesto continuo que UIPanGestureRecognizer
el dedo del usuario, puede usar UIPanGestureRecognizer
lugar de UISwipeGestureRecognizer
, y creo que animateWithDuration
es mejor que CATransition
en ese caso. handlePanGesture
el handlePanGesture
para cambiar las coordenadas del frame
para coordinar con el gesto del usuario, y luego modifiqué el código anterior para completar la animación cuando el usuario la soltó. Funciona bastante bien No creo que puedas hacer eso con CATransition
muy fácilmente.
Por ejemplo, puede crear un controlador de gestos en la vista principal del controlador:
[self.view addGestureRecognizer:[[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)]];
Y el manejador podría verse como:
- (void)handlePan:(UIPanGestureRecognizer *)gesture
{
// transform the three views by the amount of the x translation
CGPoint translate = [gesture translationInView:gesture.view];
translate.y = 0.0; // I''m just doing horizontal scrolling
prevView.frame = [self frameForPreviousViewWithTranslate:translate];
currView.frame = [self frameForCurrentViewWithTranslate:translate];
nextView.frame = [self frameForNextViewWithTranslate:translate];
// if we''re done with gesture, animate frames to new locations
if (gesture.state == UIGestureRecognizerStateCancelled ||
gesture.state == UIGestureRecognizerStateEnded ||
gesture.state == UIGestureRecognizerStateFailed)
{
// figure out if we''ve moved (or flicked) more than 50% the way across
CGPoint velocity = [gesture velocityInView:gesture.view];
if (translate.x > 0.0 && (translate.x + velocity.x * 0.25) > (gesture.view.bounds.size.width / 2.0) && prevView)
{
// moving right (and/or flicked right)
[UIView animateWithDuration:0.25
delay:0.0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
prevView.frame = [self frameForCurrentViewWithTranslate:CGPointZero];
currView.frame = [self frameForNextViewWithTranslate:CGPointZero];
}
completion:^(BOOL finished) {
// do whatever you want upon completion to reflect that everything has slid to the right
// this redefines "next" to be the old "current",
// "current" to be the old "previous", and recycles
// the old "next" to be the new "previous" (you''d presumably.
// want to update the content for the new "previous" to reflect whatever should be there
UIView *tempView = nextView;
nextView = currView;
currView = prevView;
prevView = tempView;
prevView.frame = [self frameForPreviousViewWithTranslate:CGPointZero];
}];
}
else if (translate.x < 0.0 && (translate.x + velocity.x * 0.25) < -(gesture.view.frame.size.width / 2.0) && nextView)
{
// moving left (and/or flicked left)
[UIView animateWithDuration:0.25
delay:0.0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
nextView.frame = [self frameForCurrentViewWithTranslate:CGPointZero];
currView.frame = [self frameForPreviousViewWithTranslate:CGPointZero];
}
completion:^(BOOL finished) {
// do whatever you want upon completion to reflect that everything has slid to the left
// this redefines "previous" to be the old "current",
// "current" to be the old "next", and recycles
// the old "previous" to be the new "next". (You''d presumably.
// want to update the content for the new "next" to reflect whatever should be there
UIView *tempView = prevView;
prevView = currView;
currView = nextView;
nextView = tempView;
nextView.frame = [self frameForNextViewWithTranslate:CGPointZero];
}];
}
else
{
// return to original location
[UIView animateWithDuration:0.25
delay:0.0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
prevView.frame = [self frameForPreviousViewWithTranslate:CGPointZero];
currView.frame = [self frameForCurrentViewWithTranslate:CGPointZero];
nextView.frame = [self frameForNextViewWithTranslate:CGPointZero];
}
completion:NULL];
}
}
}
Que utiliza estos métodos de frame
simples que probablemente definirías para tu UX deseada:
- (CGRect)frameForPreviousViewWithTranslate:(CGPoint)translate
{
return CGRectMake(-self.view.bounds.size.width + translate.x, translate.y, self.view.bounds.size.width, self.view.bounds.size.height);
}
- (CGRect)frameForCurrentViewWithTranslate:(CGPoint)translate
{
return CGRectMake(translate.x, translate.y, self.view.bounds.size.width, self.view.bounds.size.height);
}
- (CGRect)frameForNextViewWithTranslate:(CGPoint)translate
{
return CGRectMake(self.view.bounds.size.width + translate.x, translate.y, self.view.bounds.size.width, self.view.bounds.size.height);
}
Su implementación particular, sin duda, variará, pero espero que esto ilustre la idea.
Habiendo ilustrado todo esto (complementando y clarificando esta vieja respuesta), debo señalar que ya no uso esta técnica. Hoy en día, generalmente uso un UIScrollView
(con "paginación" activada) o (en iOS 6) un UIPageViewController
. Esto le quita el negocio de escribir este tipo de gestor de gestos (y disfrutar de funciones adicionales como barras de desplazamiento, rebotes, etc.). En la implementación de UIScrollView
, solo respondo al evento scrollViewDidScroll
para asegurarme de que estoy cargando perezosamente la subvista necesaria.
Podrías crear una animación de CATransition
. Este es un ejemplo de cómo puede deslizar una segunda vista (desde la izquierda) en la pantalla mientras presiona la vista actual:
UIView *theParentView = [self.view superview];
CATransition *animation = [CATransition animation];
[animation setDuration:0.3];
[animation setType:kCATransitionPush];
[animation setSubtype:kCATransitionFromLeft];
[animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
[theParentView addSubview:yourSecondViewController.view];
[self.view removeFromSuperview];
[[theParentView layer] addAnimation:animation forKey:@"showSecondViewController"];
Si desea el mismo efecto de desplazamiento / desplazamiento de página que tiene el trampolín al cambiar de página, ¿por qué no usar un UIScrollView
?
CGFloat width = 320;
CGFloat height = 400;
NSInteger pages = 3;
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0,0,width,height)];
scrollView.contentSize = CGSizeMake(width*pages, height);
scrollView.pagingEnabled = YES;
Y luego use UIPageControl
para obtener estos puntos. :)