English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Detalles de cómo personalizar la animación de transición de controlador en iOS (push)

Introducción

Recientemente, he tenido algo de tiempo libre, he organizado algunos proyectos recientes que he hecho, este artículo introduce principalmente el contenido relacionado con la animación de transición personalizada de controlador de iOS push, lo comparto para que todos lo refieran y aprendan, no hay mucho que decir aquí, vamos a ver la introducción detallada juntos.

Imágenes del efecto:


iOS7 Apple lanzó la API de transición personalizada. Desde entonces, cualquier animación que se pueda implementar con CoreAnimation puede aparecer entre el cambio de dos ViewController. Y la forma de implementación es altamente desacoplada, lo que significa que al mismo tiempo que se garantiza la limpieza del código, si se desea reemplazar otro esquema de animación, solo es necesario cambiar el nombre de una clase, lo que realmente es una experiencia de placer de código de alta apariencia.

En realidad, hay muchos tutoriales en línea sobre animaciones de transición personalizadas, aquí espero que los estudiantes puedan entender y comenzar fácilmente.

Existen dos tipos de transiciones de pantalla: Push y Modal, por lo que las animaciones de transición personalizadas también se dividen en dos tipos. Hoy hablaremos sobre Push

animación de transición personalizada Push

Primero, configure la interfaz de usuario, agregue4número de botones:

- (void)addButton{  
 self.buttonArr = [NSMutableArray array];  
 CGFloat margen=50;
 CGFloat width=(self.view.frame.size.width-margen*3)/2;
 CGFloat height = width;
 CGFloat x = 0;
 CGFloat y = 0;
 //columnas
 NSInteger col = 2;
 for (NSInteger i = 0; i < 4; i ++) {   
  x = margen + (i%col)*(margen+width);
  y = margen + (i/col)*(margen+height) + 150;
  UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
  button.frame = CGRectMake(x, y, width, height);
  button.layer.cornerRadius = width * 0.5;
  [button addTarget:self action:@selector(btnclick:) forControlEvents:UIControlEventTouchUpInside];
  button.backgroundColor = [UIColor colorWithRed:arc4random()%255/255.0 green:arc4random()%255/255.0 blue:arc4random()%255/255.0 alpha:1.0];
  button.tag = i+1;
  [self.view addSubview:button];
  [self.buttonArr addObject:button];
 }
}

Añadir animación:

- (void)setupButtonAnimation{  
 [self.buttonArr enumerateObjectsUsingBlock:^(UIButton * _Nonnull button, NSUInteger idx, BOOL * _Nonnull stop) {   
  // positionAnimation
  CAKeyframeAnimation *positionAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
  positionAnimation.calculationMode = kCAAnimationPaced;
  positionAnimation.fillMode = kCAFillModeForwards;
  positionAnimation.repeatCount = MAXFLOAT;
  positionAnimation.autoreverses = YES;
  positionAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
  positionAnimation.duration = (idx == self.buttonArr.count - 1);63; 4 : 5+idx;
  UIBezierPath *positionPath = [UIBezierPath bezierPathWithOvalInRect:CGRectInset(button.frame, button.frame.size.width/2-5, button.frame.size.height/2-5);
  positionAnimation.path = positionPath.CGPath;
  [button.layer addAnimation:positionAnimation forKey:nil];   
  // scaleXAniamtion
  CAKeyframeAnimation *scaleXAniamtion = [CAKeyframeAnimation animationWithKeyPath:@"transform.scale.x"];
  scaleXAniamtion.values = @[@1.0, @1.1,@1.0];
  scaleXAniamtion.keyTimes = @[@0.0, @0.0, @5,@1.0];
  scaleXAniamtion.repeatCount = MAXFLOAT;
  scaleXAniamtion.autoreverses = YES;
  scaleXAniamtion.duration = 4+idx;
  [button.layer addAnimation:scaleXAniamtion forKey:nil];   
  // scaleYAniamtion
  CAKeyframeAnimation *scaleYAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform.scale.y"];
  scaleYAnimation.values = @[@1,@1.1,@1.0];
  scaleYAnimation.keyTimes = @[@0.0,@0.5,@1.0];
  scaleYAnimation.autoreverses = YES;
  scaleYAnimation.repeatCount = YES;
  scaleYAnimation.duration = 4+idx;
  [button.layer addAnimation:scaleYAnimation forKey:nil];   
 };
}

La estructura de la interfaz está lista:

Luego, para implementar una animación de transición personalizada en Push, primero debe cumplir con el protocolo UINavigationControllerDelegate

Apple proporciona varios métodos de protocolo en UINavigationControllerDelegate, a través de los tipos de retorno se puede saber claramente la función específica de cada uno.

//usado para personalizar la animación de transición
- (nullable id)navigationController:(UINavigationController *)navigationController         animationControllerForOperation:(UINavigationControllerOperation)operation            fromViewController:(UIViewController *)fromVC             toViewController:(UIViewController *)toVC NS_AVAILABLE_IOS(7_0);
//Añadir interacción de usuario para esta animación
- (nullable id)navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController:(id) animationController NS_AVAILABLE_IOS(7_0);

En el primer método, simplemente debe devolver un objeto que cumpla con el protocolo UIViewControllerInteractiveTransitioning y realizar la animación dentro de él.

  • Crear una clase de animación que herede de NSObject y declare UIViewControllerAnimatedTransitioning.
  • Sobrecargar el método del protocolo en UIViewControllerAnimatedTransitioning.
//Regresar el tiempo de animación
- (NSTimeInterval)transitionDuration:(nullable id)transitionContext;
//Escriba el código de animación dentro de él
- (void)animateTransition:(id)transitionContext;

Primero, personalizo una clase llamada LRTransitionPushController que hereda de NSObject y cumple con el protocolo UIViewControllerAnimatedTransitioning

 - (void)animateTransition:(id)transitionContext{  
 self.transitionContext = transitionContext;  
 //Obtener el controlador de origen: no escriba UITransitionContextFromViewKey
 LRTransitionPushController *fromVc = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
 //Obtener el controlador de destino: no escriba UITransitionContextToViewKey
 LRTransitionPopController *toVc = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];  
 //Obtener la vista del contenedor
 UIView *containView = [transitionContext containerView];
 // Todos se agregan al contenedor. Notar el orden: la vista del controlador de destino debe agregarse después
 [containView addSubview:fromVc.view];
 [containView addSubview:toVc.view];  
 UIButton *button = fromVc.button;
 //Dibujar círculo
 UIBezierPath *startPath = [UIBezierPath bezierPathWithOvalInRect:button.frame];
 //Crear dos instancias de UIBezierPath circulares; una con el tamaño del botón y otra con un radio suficiente para cubrir toda la pantalla. La animación final se realiza entre estos dos caminos bezier.
 //Punto del ángulo más lejano del centro del botón desde la pantalla
 CGPoint finalPoint;
 //Determinar en qué cuadrante está el punto de activación
 if(button.frame.origin.x > (toVc.view.bounds.size.width / 2)) {
  if (button.frame.origin.y < (toVc.view.bounds.size.height / 2)) {
   //Primer cuadrante
   finalPoint = CGPointMake(0, CGRectGetMaxY(toVc.view.frame));
  }
   //Cuarto cuadrante
   finalPoint = CGPointMake(0, 0);
  }
 }
  if (button.frame.origin.y < (toVc.view.bounds.size.height / 2)) {
   //Segundo cuadrante
   finalPoint = CGPointMake(CGRectGetMaxX(toVc.view.frame), CGRectGetMaxY(toVc.view.frame));
  }
   //Tercer cuadrante
   finalPoint = CGPointMake(CGRectGetMaxX(toVc.view.frame), 0);
  }
 } 
 CGPoint startPoint = CGPointMake(button.center.x, button.center.y);
 //Calcular el radio de expansión hacia afuera = distancia del centro del botón al ángulo más lejano de la pantalla - Radio del botón
 CGFloat radius = sqrt((finalPoint.x-startPoint.x) * (finalPoint.x-startPoint.x) + (finalPoint.y-startPoint.y) * (finalPoint.y-startPoint.y)) - sqrt(button.frame.size.width/2 * button.frame.size.width/2 + button.frame.size.height/2 * button.frame.size.height/2);
 UIBezierPath *endPath = [UIBezierPath bezierPathWithOvalInRect:CGRectInset(button.frame, -radius, -radius)];  
 //赋值给toVc视图layer的mask
 CAShapeLayer *maskLayer = [CAShapeLayer layer];
 maskLayer.path = endPath.CGPath;
 toVc.view.layer.mask = maskLayer;
 CABasicAnimation *maskAnimation =[CABasicAnimation animationWithKeyPath:@"path"];
 maskAnimation.fromValue = (__bridge id)startPath.CGPath;
 maskAnimation.toValue = (__bridge id)endPath.CGPath;
 maskAnimation.duration = [self transitionDuration:transitionContext];
 maskAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
 maskAnimation.delegate = self;
 [maskLayer addAnimation:maskAnimation forKey:@"path"]; 
}

在控制器里面用来自定义转场动画的方法里返回刚才自定义的动画类

- (id)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC{  
 if (operation == UINavigationControllerOperationPush) {
  return [LRTranstionAnimationPush new];
 }
  return nil;
 }
}

Hasta aquí, la animación de transición personalizada se ha completado

La animación de pop solo hace lo contrario de la animación de push, aquí no se explicará en detalle, si hay dudas, se puede ver el código

Agregar gesto de retorno de deslizamiento

Como se mencionó anteriormente, este método es para agregar interacción de usuario a esta animación, por lo que debemos implementar el retorno de deslizamiento en el pop

La manera más sencilla debería ser utilizar la clase UIPercentDrivenInteractiveTransition proporcionada por UIKit, esta clase ya ha implementado el protocolo UIViewControllerInteractiveTransitioning, los estudiantes pueden usar el objeto de esta clase para especificar el porcentaje de finalización de la animación de transición.

//Añadir interacción de usuario para esta animación
- (nullable id)navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController:(id) animationController NS_AVAILABLE_IOS(7_0);

Primer paso: agregar el gesto

 UIPanGestureRecognizer *gestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
 [self.view addGestureRecognizer:gestureRecognizer];

Segundo paso: determine la proporción de ejecución de la animación a través de los cambios en el deslizamiento del usuario

- (void)handlePan:(UIPanGestureRecognizer *)gestureRecognizer {  
 /*Llamar al método updateInteractiveTransition: de UIPercentDrivenInteractiveTransition puede controlar hasta dónde avanza la animación de transición,
  Cuando el gesto de desplazamiento hacia abajo del usuario se completa, llame a finishInteractiveTransition o cancelInteractiveTransition, UIKit ejecutará automáticamente la mitad restante del animación,
  o permita que el animación regrese a su estado inicial.*/   
 if ([gestureRecognizer translationInView:self.view].x>=0) {
  //Proporción de deslizamiento de手势
  CGFloat per = [gestureRecognizer translationInView:self.view].x / (self.view.bounds.size.width);
  per = MIN(1.0, (MAX(0.0, per)));   
  if (gestureRecognizer.state == UIGestureRecognizerStateBegan) {    
   self.interactiveTransition = [UIPercentDrivenInteractiveTransition new];
   [self.navigationController popViewControllerAnimated:YES];    
  } else if (gestureRecognizer.state == UIGestureRecognizerStateChanged){    
   if([gestureRecognizer translationInView:self.view].x ==0){     
    [self.interactiveTransition updateInteractiveTransition:0.01;     
   }     
    [self.interactiveTransition updateInteractiveTransition:per];
   }    
  } else if (gestureRecognizer.state == UIGestureRecognizerStateEnded || gestureRecognizer.state == UIGestureRecognizerStateCancelled){    
   if([gestureRecognizer translationInView:self.view].x == 0){     
    [self.interactiveTransition cancelInteractiveTransition];
    self.interactiveTransition = nil;     
   }5) {     
    [ self.interactiveTransition finishInteractiveTransition];
   }
    [ self.interactiveTransition cancelInteractiveTransition];
   }
   self.interactiveTransition = nil;
  }     
 } else if (gestureRecognizer.state == UIGestureRecognizerStateChanged){
  [self.interactiveTransition updateInteractiveTransition:0.01;
  [self.interactiveTransition cancelInteractiveTransition]; 
 } else if ((gestureRecognizer.state == UIGestureRecognizerStateEnded || gestureRecognizer.state == UIGestureRecognizerStateCancelled)){   
  self.interactiveTransition = nil;
 }  
}

Paso 3: En el método de agente de interacción del usuario para agregar animación, devuelva una instancia de UIPercentDrivenInteractiveTransition

- (id)navigationController:(UINavigationController *navigationController       interactionControllerForAnimationController:(id) animationController {
 return self.interactiveTransition;
}

Si sienten que este artículo les ha sido útil, denle un 'me gusta', gracias.

El código se colocó enGitHubAquí pueden descargar, por supuesto, también pueden hacerlo a través deDescarga local

Resumen

Eso es todo el contenido de este artículo. Espero que el contenido de este artículo tenga un valor de referencia y aprendizaje para todos. Si tienen alguna pregunta, pueden dejar comentarios para intercambiar. Gracias por el apoyo a la tutorial de gritos.

Declaración: El contenido de este artículo se obtiene de la red, es propiedad del autor original, el contenido es contribuido y subido por los usuarios de Internet de manera autónoma. Este sitio no posee los derechos de propiedad, no ha sido editado por humanos y no asume ninguna responsabilidad legal relacionada. Si encuentra contenido sospechoso de infracción de derechos de autor, por favor envíe un correo electrónico a: notice#oldtoolbag.com (al enviar un correo electrónico, por favor reemplace # con @) para denunciar, y proporcione la evidencia relevante. Una vez que se verifique, este sitio eliminará inmediatamente el contenido sospechoso de infracción.

Te gustará