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

Explicação detalhada de como personalizar a animação de transição de controle no iOS push

Prefácio

Recentemente, tive algum tempo livre, organizei alguns projetos recentes, este artigo introduz principalmente o conteúdo sobre a transição de animação personalizada de controleador iOS push, compartilho para referência e aprendizado de todos, não vou dizer muito, vamos juntos ver a introdução detalhada.

Efeito da imagem:


iOS7 O Apple lançou a API de transição personalizada. Desde então, qualquer animação que possa ser implementada com CoreAnimation pode aparecer na troca entre dois ViewController. E a maneira de implementação é altamente desacoplada, o que também significa que, para substituir outros esquemas de animação, é só mudar o nome de uma classe, realmente sentindo o prazer de código de alta qualidade.

Na verdade, há muitos tutoriais sobre animação de transição personalizada na internet, aqui espero que os alunos possam entender e aprender facilmente.

A transição pode ser de dois tipos, Push e Modal, então a animação de transição personalizada também pode ser de dois tipos. Hoje vamos falar sobre Push

animação de transição personalizada Push

Primeiro, construa a interface, adicione4número de botões:

- (void)addButton{  
 self.buttonArr = [NSMutableArray array];  
 CGFloat margin=50;
 CGFloat width=(self.view.frame.size.width-margin*3)/2;
 CGFloat height = width;
 CGFloat x = 0;
 CGFloat y = 0;
 //coluna
 NSInteger col = 2;
 for (NSInteger i = 0; i < 4; i ++) {   
  x = margin + (i%col)*(margin+width);
  y = margin + (i/col)*(margin+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];
 }
}

Adicionar animação:

- (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, @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];   
 };
}

A estrutura da interface está pronta:

Para implementar uma animação de transição personalizada no Push, é necessário seguir o protocolo UINavigationControllerDelegate

A Apple fornece várias métodos de protocolo em UINavigationControllerDelegate, e é claro o efeito específico de cada um pelo tipo de retorno

//usado para personalizar a animação de transição
- (nullable id)navigationController:(UINavigationController *)navigationController         animationControllerForOperation:(UINavigationControllerOperation)operation            fromViewController:(UIViewController *)fromVC             toViewController:(UIViewController *toVC NS_AVAILABLE_IOS(7_0);
//Adicionar interação de usuário para essa animação
- (nullable id)navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController:(id) animationController NS_AVAILABLE_IOS(7_0);

No primeiro método, basta retornar um objeto que siga o protocolo UIViewControllerInteractiveTransitioning e implementar a animação dentro dele

  • Crie uma classe de animação que herda de NSObject e declara UIViewControllerAnimatedTransitioning.
  • Sobrecarregar o método do protocolo na classe UIViewControllerAnimatedTransitioning.
//Retornar o tempo de animação
- (NSTimeInterval)transitionDuration:(nullable id)transitionContext;
//Escreva o código de animação dentro dela
- (void)animateTransition:(id)transitionContext;

Primeiro, eu crio uma classe chamada LRTransitionPushController que herda de NSObject e cumpre o protocolo UIViewControllerAnimatedTransitioning

 - (void)animateTransition:(id)transitionContext{  
 self.transitionContext = transitionContext;  
 //Obter o controlador de origem - atenção para não escrever UITransitionContextFromViewKey
 LRTransitionPushController *fromVc = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
 //Obter o controlador de destino - atenção para não escrever UITransitionContextToViewKey
 LRTransitionPopController *toVc = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];  
 //Obter a vista do contêiner
 UIView *containView = [transitionContext containerView];
 // Todas são adicionadas ao container. Atente para a ordem - a view do controlador de destino precisa ser adicionada no final
 [containView addSubview:fromVc.view];
 [containView addSubview:toVc.view];  
 UIButton *button = fromVc.button;
 //Desenhar círculo
 UIBezierPath *startPath = [UIBezierPath bezierPathWithOvalInRect:button.frame];
 //Criar duas instâncias de UIBezierPath circular; uma com o tamanho do botão e outra com um raio suficiente para cobrir a tela. A animação final ocorre entre esses dois caminhos bezier.
 //Ponto do canto mais distante da tela do centro do botão
 CGPoint finalPoint;
 //Determinar em qual quadrante está o ponto de trigger
 if(button.frame.origin.x > (toVc.view.bounds.size.width / 2)) {
  if (button.frame.origin.y < (toVc.view.bounds.size.height / 2)) {
   //Primeiro quadrante
   finalPoint = CGPointMake(0, CGRectGetMaxY(toVc.view.frame));
  }else{
   //Quarto quadrante
   finalPoint = CGPointMake(0, 0);
  }
 }else{
  if (button.frame.origin.y < (toVc.view.bounds.size.height / 2)) {
   //Segundo quadrante
   finalPoint = CGPointMake(CGRectGetMaxX(toVc.view.frame), CGRectGetMaxY(toVc.view.frame));
  }else{
   //Terceiro quadrante
   finalPoint = CGPointMake(CGRectGetMaxX(toVc.view.frame), 0);
  }
 } 
 CGPoint startPoint = CGPointMake(button.center.x, button.center.y);
 //Calcular o raio de expansão externa = distância do centro do botão ao canto mais distante da tela - Raios do botão
 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)];  
 //atribuir o valor de mask para a camada de visão do toVc
 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"]; 
}

em um método personalizado de transição de animação no controlador, retornar a classe de animação personalizada anterior

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

Até aqui, a animação de transição personalizada está concluída

A animação de pop apenas faz o contrário da animação de push, não entrarei em detalhes aqui, se tiver dúvidas, pode verificar o código

Adicionar gesto de retorno de deslize

Como mencionado anteriormente, esse método é para adicionar interação de usuário para essa animação, então temos que implementar o retorno de deslize ao pop

A maneira mais simples deve ser usar a classe UIPercentDrivenInteractiveTransition fornecida pelo UIKit, essa classe já implementou o protocolo UIViewControllerInteractiveTransitioning, os alunos podem usar o objeto dessa classe para especificar a porcentagem de conclusão da animação de transição.

//Adicionar interação de usuário para essa animação
- (nullable id)navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController:(id) animationController NS_AVAILABLE_IOS(7_0);

Primeiro passo: adicionar手势

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

Segundo passo: determine a proporção da execução da animação com base na variação do deslize do usuário

- (void)handlePan:(UIPanGestureRecognizer *)gestureRecognizer {  
 /*A chamada do método updateInteractiveTransition: da UIPercentDrivenInteractiveTransition pode controlar até onde a animação de transição ocorre,
  Quando o gesto de arrastar para baixo do usuário é completado, chame finishInteractiveTransition ou cancelInteractiveTransition, o UIKit executará automaticamente a metade restante da animação,
  ou volte a animação para o estado inicial.*/   
 if ([gestureRecognizer translationInView:self.view].x>=0) {
  //Proporção de deslize 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];     
   }else{     
    [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;     
   }else if (per > 0.5) {     
    [ self.interactiveTransition finishInteractiveTransition];
   }else{
    [ 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;
 }  
}

Passo 3: No método de agente de interação do usuário para a animação, retorne uma instância de UIPercentDrivenInteractiveTransition

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

Se você achar que este artigo foi útil para você, dê um gostei, obrigado!

O código está emGitHubVocê pode baixar aqui, claro, também podeDownload Local

Resumo

Isso é tudo que há neste artigo, esperamos que o conteúdo deste artigo tenha algum valor de referência para seu aprendizado ou trabalho. Se tiver alguma dúvida, pode deixar um comentário para trocar ideias, obrigado pelo apoio ao tutorial de gritos.

Declaração: O conteúdo deste artigo é extraído da Internet, pertence ao respectivo proprietário, o conteúdo é contribuído e carregado voluntariamente pelos usuários da Internet, o site não possui direitos de propriedade, não foi editado manualmente e não assume responsabilidade legal relevante. Se você encontrar conteúdo suspeito de violação de direitos autorais, envie um e-mail para: notice#oldtoolbag.com (ao enviar e-mail, substitua # por @ para denunciar e forneça provas relevantes. Se confirmado, o site deletará imediatamente o conteúdo suspeito de violação de direitos autorais.)

Você também pode gostar