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

Détails de la personnalisation de l'animation de transition de contrôleur iOS push

Préambule

Récemment, j'ai eu du temps libre, j'ai organisé les projets que j'ai réalisés récemment. Cet article présente principalement le contenu concernant les transitions d'animation personnalisées de contrôleur iOS push, et je le partage pour que chacun puisse l'étudier et l'utiliser. Je ne vais pas en dire plus, regardons ensemble la présentation détaillée.

Illustration :


iOS7 Apple a lancé l'API de transition personnalisée. Depuis lors, toute animation pouvant être réalisée avec CoreAnimation peut apparaître lors du basculement entre deux ViewController. De plus, la méthode d'implémentation est fortement découplée, ce qui signifie que pour remplacer d'autres schémas d'animation tout en garantissant la propreté du code, il suffit de modifier simplement un nom de classe, ce qui permet vraiment de ressentir le plaisir apporté par le code à haute valeur ajoutée.

En réalité, il y a beaucoup de tutoriels en ligne sur l'animation de transition personnalisée, mais j'espère que les étudiants pourront les comprendre et les utiliser facilement.

Il existe deux types de transition de phase : Push et Modal, donc l'animation de transition personnalisée est également divisée en deux types. Aujourd'hui, nous allons parler de Push

animation de transition personnalisée Push

Tout d'abord, construire l'interface et ajouter4des boutons :

- (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;
 //列
 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];
 }
}

Ajouter l'animation :

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

La structure de l'interface est prête :

Pour implémenter une animation de transition personnalisée lors d'un Push, il faut respecter un protocole : UINavigationControllerDelegate

Apple a fourni plusieurs méthodes de protocole dans UINavigationControllerDelegate, où le type de retour peut clairement indiquer l'effet spécifique de chacun.

//utilisé pour personnaliser l'animation de transition
- (nullable id)navigationController:(UINavigationController *)navigationController         animationControllerForOperation:(UINavigationControllerOperation)operation            fromViewController:(UIViewController *)fromVC             toViewController:(UIViewController *)toVC NS_AVAILABLE_IOS(7_0);
//Ajouter l'interaction utilisateur pour cette animation
- (nullable id)navigationController:(UINavigationController *)navigationController       interactionControllerForAnimationController:(id) animationController NS_AVAILABLE_IOS(7_0);

Dans la première méthode, il suffit de retourner un objet respectant l'accord UIViewControllerInteractiveTransitioning et d'y implémenter l'animation.

  • Créer une classe d'animation héritant de NSObject et déclarant UIViewControllerAnimatedTransitioning
  • Redéfinir la méthode de protocole de UIViewControllerAnimatedTransitioning
//Retourner le temps d'animation
- (NSTimeInterval)transitionDuration:(nullable id)transitionContext;
//Écrivez le code d'animation à l'intérieur
- (void)animateTransition:(id)transitionContext;

Tout d'abord, j'ai personnalisé une classe nommée LRTransitionPushController qui hérite de NSObject et obéit à l'accord de UIViewControllerAnimatedTransitioning

 - (void)animateTransition:(id)transitionContext{  
 self.transitionContext = transitionContext;  
 //Obtenir le contrôleur source, attention à ne pas écrire UITransitionContextFromViewKey
 LRTransitionPushController *fromVc = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
 //Obtenir le contrôleur cible, attention à ne pas écrire UITransitionContextToViewKey
 LRTransitionPopController *toVc = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];  
 //Obtenir la vue de conteneur
 UIView *containView = [transitionContext containerView];
 // Tous les deux sont ajoutés au container. Notez l'ordre : la vue du contrôleur cible doit être ajoutée en dernier
 [containView addSubview:fromVc.view];
 [containView addSubview:toVc.view];  
 UIButton *button = fromVc.button;
 //dessiner un cercle
 UIBezierPath *startPath = [UIBezierPath bezierPathWithOvalInRect:button.frame];
 //Créer deux instances de UIBezierPath circulaires ; l'une pour la taille du bouton, l'autre avec un rayon suffisant pour couvrir l'écran. L'animation finale se déroule entre ces deux chemins de Bézier.
 //point du coin le plus éloigné de l'écran par rapport au centre du bouton
 CGPoint finalPoint;
 //décider dans quel quadrant le point déclencheur se trouve
 if(button.frame.origin.x > (toVc.view.bounds.size.width / 2)) {
  if (button.frame.origin.y < (toVc.view.bounds.size.height / 2)) {
   //premier quadrant
   finalPoint = CGPointMake(0, CGRectGetMaxY(toVc.view.frame));
  }else{
   //quatrième quadrant
   finalPoint = CGPointMake(0, 0);
  }
 }else{
  if (button.frame.origin.y < (toVc.view.bounds.size.height / 2)) {
   //deuxième quadrant
   finalPoint = CGPointMake(CGRectGetMaxX(toVc.view.frame), CGRectGetMaxY(toVc.view.frame));
  }else{
   //troisième quadrant
   finalPoint = CGPointMake(CGRectGetMaxX(toVc.view.frame), 0);
  }
 } 
 CGPoint startPoint = CGPointMake(button.center.x, button.center.y);
 //Calculer le rayon de diffusion vers l'extérieur = la distance entre le centre du bouton et le coin le plus éloigné de l'écran - rayon du bouton
 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)];  
 //affecter la valeur de mask au layer de la vue 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"]; 
}

retourner la méthode utilisée pour personnaliser l'animation de transition en fonction du contrôleur

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

Jusqu'à présent, l'animation de transition personnalisée est terminée

L'animation de pop ne fait que faire l'inverse de l'animation de push, je ne vais pas entrer dans les détails ici, ceux qui ont des questions peuvent consulter le code

Ajouter le geste de retour glissant

Comme mentionné précédemment, cette méthode ajoute l'interaction utilisateur pour cette animation, donc nous devons réaliser le retour glissant lors du pop

La manière la plus simple devrait être d'utiliser la classe UIPercentDrivenInteractiveTransition fournie par UIKit, cette classe a déjà implémenté l'accord UIPercentDrivenInteractiveTransition, les étudiants peuvent utiliser l'objet de cette classe pour spécifier le pourcentage de fin de l'animation de transition.

//Ajouter l'interaction utilisateur pour cette animation
- (nullable id)navigationController:(UINavigationController *)navigationController       interactionControllerForAnimationController:(id) animationController NS_AVAILABLE_IOS(7_0);

Première étape : ajouter le geste

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

Deuxième étape : déterminer la proportion de l'animation à exécuter en fonction des changements de glissement de l'utilisateur

- (void)handlePan:(UIPanGestureRecognizer *)gestureRecognizer {  
 /*Appeler la méthode updateInteractiveTransition: de UIPercentDrivenInteractiveTransition permet de contrôler jusqu'où l'animation de transition se déroule,
  Lorsque le geste de défilement du utilisateur est terminé, appeler finishInteractiveTransition ou cancelInteractiveTransition, UIKit exécutera automatiquement la moitié restante de l'animation,
  ou revenir à l'état initial de l'animation.*/   
 si ([gestureRecognizer translationInView:self.view].x>=0) {
  //比例 de glissement de geste
  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;
 }  
}

Étape 3 : Retournez l'instance de UIPercentDrivenInteractiveTransition dans la méthode d'agent utilisateur ajoutée pour l'interaction avec l'animation

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

Si vous trouvez cet article utile, cliquez sur "J'aime", merci beaucoup

Le code est placé dansGitHubVous pouvez télécharger ici, bien sûr, vous pouvez également passer parTéléchargement local

Résumé

Voici la totalité du contenu de cet article, j'espère que le contenu de cet article a une certaine valeur de référence pour votre apprentissage ou travail. Si vous avez des doutes, vous pouvez laisser des commentaires pour échanger, merci de votre soutien au tutoriel d'alarme.

Déclaration : le contenu de cet article est issu du réseau, propriété de l'auteur original, contribué et téléversé par les utilisateurs d'Internet, ce site ne détient pas de propriété, n'a pas été traité par l'édition humaine et n'assume pas de responsabilité juridique connexe. Si vous trouvez du contenu suspect de violation de droits d'auteur, veuillez envoyer un e-mail à : notice#oldtoolbag.com (veuillez remplacer # par @ lors de l'envoi d'un e-mail pour signaler une violation, et fournir des preuves pertinentes. Une fois vérifié, ce site supprimera immédiatement le contenu suspect de violation de droits d'auteur.)

Vous pourriez aussi aimer