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