Revista Informática

iOS 7 Decodificado: Transiciones personalizadas. Parte 2

Publicado el 06 diciembre 2013 por Codehero @codeheroblog

Despedir controladores

Para despedir los controladores con animaciones personalizadas es mucho mas sencillo en iOS 7 y bastante parecido a la presentación de los mismos (que estudiamos en el el capítulo anterior iOS 7 Decodificado: Transiciones personalizadas.

Lo primero que debemos saber es que el UIViewController que presenta al controlador es el encargado tanto de la animación al mostrarlo como al despedirlo. Para realizar la animación de despedida de una vista de forma animada, nos ubicamos en el Controlador encargado de manejar la animación y agregamos el siguiente método del delegate UIViewControllerTransitioningDelegate

- (id < UIViewControllerAnimatedTransitioning >)animationControllerForDismissedController:(UIViewController *)dismissed { // retornamos el metodo UIViewControllerAnimatedTransitioning }

1234 -(id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed{// retornamos el metodo UIViewControllerAnimatedTransitioning}

Luego de esto prácticamente seguimos los pasos de la animaciones estudiados en el capítulo anterior.

Crear una clase de transición animada que controlará el movimiento de los controladores

En esta etapa creamos la animación de la transición para despedir controladores, nuestro caso seria algo así:

  • DismissTransition.h
@interface DismissTransition : NSObject <UIViewControllerAnimatedTransitioning> @end

12 @interfaceDismissTransition:NSObject<UIViewControllerAnimatedTransitioning>@end

  • DismissTransition.m
#import "DismissTransition.h" @implementation DismissTransition - (NSTimeInterval) transitionDuration:(id)transitionContext { return 1.0f; } - (void) animateTransition:(id)transitionContext { // Esta implementación la vamos a dejar para mas adelante } @end

123456789101112 #import "DismissTransition.h" @implementation DismissTransition-(NSTimeInterval)transitionDuration:(id)transitionContext{  return1.0f;}-(void)animateTransition:(id)transitionContext{  // Esta implementación la vamos a dejar para mas adelante}@end

Implementar el delegado de la transición

En esta etapa solo instanciamos nuestra transición en el método que implementamos en el paso número dos. Es básicamente algo así:

- (id < UIViewControllerAnimatedTransitioning >) animationControllerForDismissedController:(UIViewController *)dismissed { DismissTransition *transition = [[DismissTransition alloc] init]; return transition; }

12345 -(id<UIViewControllerAnimatedTransitioning>)  animationControllerForDismissedController:(UIViewController *)dismissed{  DismissTransition *transition=[[DismissTransition alloc]init];  returntransition;}

Implementar la animación en la clase de transición

Como ya vimos en el capítulo anterior esta es la etapa más difícil ya que debemos desarrollar el código de la animación. Para este curso haremos que la vista se eleve un poco y luego baje dandole un efecto de caída sobre su lado derecho. Veamos el código mejor:

- (void) animateTransition:(id)transitionContext { // ubicamos la vista que vamos a mover UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey]; // Ubicamos el tamanio del contexto __block CGRect presentedFrame = [transitionContext initialFrameForViewController:fromVC]; //Arrancamos con nuestra animación [UIView animateKeyframesWithDuration:1.0f delay:0.0 options:0 animations:^{ //Separamos la animación en dos //Una parte que levante la vista un poco [UIView addKeyframeWithRelativeStartTime:0.0 relativeDuration:0.8 animations:^{ fromVC.view.frame = CGRectMake( presentedFrame.origin.x, -20, presentedFrame.size.width, presentedFrame.size.height );}]; // la última parte que la deje caer haciéndola rotar unos grados [UIView addKeyframeWithRelativeStartTime:0.8 relativeDuration:0.2 animations:^{ presentedFrame.origin.y += CGRectGetHeight(presentedFrame) + 20; fromVC.view.frame = presentedFrame; fromVC.view.transform = CGAffineTransformMakeRotation(0.2); }]; } completion:^(BOOL finished) { //Finalizamos la transición [transitionContext completeTransition:YES]; }]; }

123456789101112131415161718192021222324252627282930313233 -(void)animateTransition:(id)transitionContext{  // ubicamos la vista que vamos a mover  UIViewController *fromVC=[transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];      // Ubicamos el tamanio del contexto  __block CGRect presentedFrame=[transitionContext initialFrameForViewController:fromVC];    //Arrancamos con nuestra animación  [UIView animateKeyframesWithDuration:1.0fdelay:0.0options:0animations:^{  //Separamos la animación en dos  //Una parte que levante la vista un poco  [UIView addKeyframeWithRelativeStartTime:0.0relativeDuration:0.8animations:^{  fromVCviewframe=CGRectMake(   presentedFrameoriginx,   -20,   presentedFramesizewidth,   presentedFramesizeheight   );}];  // la última parte que la deje caer haciéndola rotar unos grados  [UIView  addKeyframeWithRelativeStartTime:0.8relativeDuration:0.2animations:^{    presentedFrameoriginy+=CGRectGetHeight(presentedFrame)+20;  fromVCviewframe=presentedFrame;  fromVCviewtransform=CGAffineTransformMakeRotation(0.2);  }];  }completion:^(BOOLfinished){  //Finalizamos la transición  [transitionContext completeTransition:YES];  }];  }

Con estos pequeños pasos producimos un efecto como éste, al despedir nuestros UIViewController:

No entraremos mucho en detalles con las animaciones de despedidas de los controladores, porque, como se habrán dado cuenta los que siguen la serie constantemente, es bastante parecido a la presentación que vimos en el capítulo anterior.

Transiciones Interactivas

Seguramente ya deben saber por donde viene esta rama de transiciones interactivas, consiste en simplemente ejecutar una transición seguida por un gesto, es decir, seleccionamos un View y lo movemos hasta que culmine con la transición.

Esto con estas nuevas características que introdujo Apple al mercado con iOS7, nos resulta bastante fácil y para hacerlo más sencillo aún lo vamos a separar en tres fáciles pasos.

  • Iniciar y adaptar la transición.
  • modificar e interactuar con los cambios de la interacción.
  • Finalizar o cancelar la animación.

Iniciar y adaptar la transición.

Para iniciar y adaptar la transición interactiva, lo primero que debemos hacer es crear un objeto UIPanGestureRecognizer y agregarla a la vista del controlador una vez presentado. Esto lo hacemos con las siguientes líneas de comando:

[self presentViewController:vc animated:YES completion:^{ UIPanGestureRecognizer *gesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleGesture:)]; [vc.view addGestureRecognizer:gesture]; }];

1234   [selfpresentViewController:vc animated:YES completion:^{  UIPanGestureRecognizer *gesture=[[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(handleGesture:)];  [vcview addGestureRecognizer:gesture];  }];

En este ejemplo estaremos agarrando el mismo ejemplo anterior y una vez culminada la presentación del controlador le agregamos el gesto.

Luego creamos el método que llama el selector del UIGestureReconize que posteriormente codificaremos.

- (void) handleGesture: (UIPanGestureRecognizer *)gesture { switch (gesture.state) { case UIGestureRecognizerStateBegan: { break; } case UIGestureRecognizerStateChanged:{ break; } case UIGestureRecognizerStateEnded:{ break; } case UIGestureRecognizerStateCancelled: { break; } default: break; } }

12345678910111213141516171819202122232425 -(void)handleGesture:(UIPanGestureRecognizer *)gesture{  switch(gesturestate){  caseUIGestureRecognizerStateBegan:{   break;  }  caseUIGestureRecognizerStateChanged:{    break;  }  caseUIGestureRecognizerStateEnded:{    break;  }  caseUIGestureRecognizerStateCancelled:  {    break;  }  default:    break;  }}

Por último vamos a necesitar a necesitar unas variables que vamos agregar en el .h del controlador y luego implementar un protocolo de transición en nuestra clase

-.h

@property (strong, nonatomic) UIPercentDrivenInteractiveTransition *myInteractiveTransition; @property (assign, nonatomic) BOOL interactive;

12 @property(strong,nonatomic)UIPercentDrivenInteractiveTransition *myInteractiveTransition;@property(assign,nonatomic)BOOLinteractive;

-Implementación del protocolo

- (id <UIViewControllerInteractiveTransitioning>) interactionControllerForDismissal:(id< UIViewControllerAnimatedTransitioning >)animator { if (self.interactive) { self.myInteractiveTransition = [[UIPercentDrivenInteractiveTransition alloc] init]; return self.myInteractiveTransition; } return nil; }

123456789 -(id<UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id<UIViewControllerAnimatedTransitioning>)animator{  if(selfinteractive){    selfmyInteractiveTransition=[[UIPercentDrivenInteractiveTransition alloc]init];  returnselfmyInteractiveTransition;  }  returnnil;}

¡Listo!. ya finalizamos esta etapa donde simplemente preparamos el camino para desarrollar la interacción con las transiciones de la aplicación.

modificar e interactuar con los cambios de la interacción.

En esta etapa vamos a ir evaluando y modificando la posición de la transición para que este se mueva según sea el caso. Veamos como lo hicimos:

- (void) handleGesture: (UIPanGestureRecognizer *)gesture { switch (gesture.state) { // Empieza el gestare case UIGestureRecognizerStateBegan: { // indicamos al sistema que existe una interacción self.interactive = YES; // Inndicamos al sistema que empiece a despedir el view [self dismissViewControllerAnimated:YES completion:^{ //Una vez finalizado la despedida indicamos al sistema que se termino la iteración self.interactive = NO; }]; break; } // Cambios del gesture case UIGestureRecognizerStateChanged:{ //Obtenemos datos importantes de la vista como: // vista contenedora del gesture //Punto de translación //porcentaje de tranlacion con respecto a la altura en nuestro caso UIView *view = gesture.view.superview; CGPoint translation = [gesture translationInView:view]; CGFloat percentTransitioned = (translation.y / (CGRectGetWidth(view.frame))); // Modificamos la transición con el porcentaje antes obtenido [self.myInteractiveTransition updateInteractiveTransition:-percentTransitioned]; break; } case UIGestureRecognizerStateEnded:{ break; } case UIGestureRecognizerStateCancelled: { [ cancelInteractiveTransition]; break; } default: break; } }

12345678910111213141516171819202122232425262728293031323334353637383940414243 -(void)handleGesture:(UIPanGestureRecognizer *)gesture{  switch(gesturestate){  // Empieza el gestare  caseUIGestureRecognizerStateBegan:{  // indicamos al sistema que existe una interacción  selfinteractive=YES;  // Inndicamos al sistema que empiece a despedir el view  [selfdismissViewControllerAnimated:YES completion:^{  //Una vez finalizado la despedida indicamos al sistema que se termino la iteración  selfinteractive=NO;  }];    break;  }  // Cambios del gesture  caseUIGestureRecognizerStateChanged:{  //Obtenemos datos importantes de la vista como:  // vista contenedora del gesture  //Punto de translación  //porcentaje de tranlacion con respecto a la altura en nuestro caso  UIView *view=gestureviewsuperview;  CGPoint translation=[gesture translationInView:view];  CGFloat percentTransitioned=(translationy/(CGRectGetWidth(viewframe)));    // Modificamos la transición con el porcentaje antes obtenido  [selfmyInteractiveTransition updateInteractiveTransition:-percentTransitioned];  break;  }  caseUIGestureRecognizerStateEnded:{    break;  }  caseUIGestureRecognizerStateCancelled:  {  [cancelInteractiveTransition];    break;  }  default:  break;  }}

En esta etapa nos ubicamos en el método del selector de nuestro gestureReconizer para empezar a codificarlo.

Lo primero que hacemos en el UIGestureRecognizerStateBegan es indicarle al sistema cuando el usuario esta o no en contacto con la vista.

Luego en UIGestureRecognizerStateChanged recopilamos una serie de datos para sacar el porcentaje de movimiento del gesture y asignárselo a la transición.

Finalizar o cancelar la animación

Para finalizar y cancelar la animación nos vemos en dos casos, cuando el usuario termina el movimiento para despedir la vista o simplemente cuando se arrepiente y deja la vista como estaba. Esto lo resolvemos estableciendo un punto en el porcentaje para decidir la mejor solución. vemos como lo resolvimos:

- (void) handleGesture: (UIPanGestureRecognizer *)gesture { switch (gesture.state) { // Empieza el gestare case UIGestureRecognizerStateBegan: {...} case UIGestureRecognizerStateChanged:{...} case UIGestureRecognizerStateEnded:{ if (self.myInteractiveTransition.percentComplete > 0.25) { [self.myInteractiveTransition finishInteractiveTransition]; }else{ [self.myInteractiveTransition cancelInteractiveTransition]; } break; } case UIGestureRecognizerStateCancelled: { [self.myInteractiveTransition cancelInteractiveTransition]; break; } default: break; } }

1234567891011121314151617181920212223242526 -(void)handleGesture:(UIPanGestureRecognizer *)gesture{  switch(gesturestate){  // Empieza el gestare  caseUIGestureRecognizerStateBegan:{}  caseUIGestureRecognizerStateChanged:{}  caseUIGestureRecognizerStateEnded:{    if(selfmyInteractiveTransitionpercentComplete>0.25){  [selfmyInteractiveTransition finishInteractiveTransition];  }else{  [selfmyInteractiveTransition cancelInteractiveTransition];  }    break;  }  caseUIGestureRecognizerStateCancelled:  {  [selfmyInteractiveTransition cancelInteractiveTransition];    break;  }  default:  break;  }}

Como ven esto es bastante simple, solo le indicamos a la transición, que cuando el movimiento sobrepase el 25% entonces finalizamos si no cancelamos la transición.

Aun nos queda un pequeño detalle que resolver, y es en la transición de la vista a la que estamos realizándole cambios. En nuestro caso es DismissTransition y lo resolvemos cambiando:

[transitionContext completeTransition:YES];

1 [transitionContext completeTransition:YES];

por:

[transitionContext completeTransition:![transitionContext transitionWasCancelled]];

1 [transitionContext completeTransition:![transitionContext transitionWasCancelled]];

Finalmente hemos finalizado nuestra transición personalizada e interactiva. ¿Bastante fácil verdad? queda de su parte poner en practica estos conceptos.


Conclusión

En este nueva serie estaremos estudiando los cambios y bondades que nos ofrece Apple con su nuevo sistema operativo iOS 7 a nivel de programación. En este capítulo estuvimos estudiando de forma muy básica como presentar transiciones animadas con un sencillo ejemplo que podrán descargar en nuestro repositorio en git.

Una vez más te recomiendo echarle un vistazo a la serie iOS 7 Decodificado y a mantenerte alerta a los nuevos capítulos de esta nueva serie, agradeciendo de antemano todas sus dudas y comentarios en la sección de comentarios.

¡Hasta el próximo capítulo!


Volver a la Portada de Logo Paperblog