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

Come personalizzare l'animazione di transizione del controller push in iOS

Introduzione

Negli ultimi tempi ho avuto un po' di tempo libero, ho organizzato i progetti che ho fatto di recente. Questo articolo introduce principalmente il contenuto di iOS personalizzazione del controller di transizione animazione push, lo condivido per riferimento e studio. Non c'è molto da dire, vediamo insieme la descrizione dettagliata.

Esempio di effetto:


A partire da iOS7, Apple ha introdotto l'API di transizione personalizzata. Da allora, qualsiasi animazione che può essere realizzata con CoreAnimation può apparire nella transizione tra due ViewController. E il modo di implementazione è altamente decoupled, il che significa che per mantenere il codice pulito e voler sostituire altri piani di animazione è sufficiente cambiare semplicemente un nome di classe, avendo veramente provato la gioia di un codice ad alta qualità.

In realtà ci sono molti tutorial su Internet sull'animazione di transizione personalizzata, qui spero che i miei compagni possano capirla facilmente e iniziare facilmente.

Le transizioni di transizione sono di due tipi, Push e Modal, quindi l'animazione di transizione personalizzata è anche di due tipi. Oggi parleremo di Push

Animazione di transizione personalizzata Push

Prima di tutto, costruiamo l'interfaccia e aggiungiamo 4 pulsanti:

- (void)aggiungiPulsante{  
 self.buttonArr = [NSMutableArray array];  
 CGFloat margine = 50;
 CGFloat larghezza = (self.view.frame.size.width-margine*3)/2;
 CGFloat altezza = larghezza;
 CGFloat x = 0;
 CGFloat y = 0;
 // colonne
 NSInteger col = 2;
 per (NSInteger i = 0; i < 4; i++) {   
  x = margine + (i%col)*(margine+larghezza);
  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];
 }
}

Aggiungi animazione:

- (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 = SI;
  positionAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
  positionAnimation.duration = (idx == self.buttonArr.count - 1) ? 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.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 costruzione dell'interfaccia è stata completata:

Poi, per implementare un'animazione di transizione personalizzata durante il Push, è necessario seguire un accordo UINavigationControllerDelegate

Apple ha fornito alcuni metodi di accordo in UINavigationControllerDelegate, che possono essere identificati chiaramente attraverso il tipo di restituzione.

// utilizzato per personalizzare l'animazione di transizione
- (nullable id)navigationController:(UINavigationController *)navigationController         animationControllerForOperation:(UINavigationControllerOperation)operation            fromViewController:(UIViewController *)fromVC             toViewController:(UIViewController *)toVC NS_AVAILABLE_IOS(7_0);
//Aggiungere l'interazione utente per questa animazione
- (nullable id)navigationController:(UINavigationController *)navigationController       interactionControllerForAnimationController:(id) animationController NS_AVAILABLE_IOS(7_0);

Nel primo metodo, ritorna un oggetto che rispetta l'accordo UIViewControllerInteractiveTransitioning e implementa l'animazione al suo interno.

  • Crea una classe di animazione che eredita da NSObject e dichiara UIViewControllerAnimatedTransitioning.
  • Ridefinisci il metodo dell'accordo in UIViewControllerAnimatedTransitioning.
// restituisce il tempo dell'animazione
- (NSTimeInterval)transitionDuration:(nullable id)transitionContext;
//Scrivi il codice dell'animazione all'interno di esso
- (void)animateTransition:(id)transitionContext;

Prima di tutto, ho creato una classe chiamata LRTransitionPushController che eredita da NSObject e aderisce al protocollo UCLAyoutAnimatedTransitioning

 - (void)animateTransition:(id)transitionContext{  
 self.transitionContext = transitionContext;  
 //Ottengo il controller di origine Attenzione a non scriverlo come UITransitionContextFromViewKey
 LRTransitionPushController *fromVc = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
 //Ottengo il controller di destinazione Attenzione a non scriverlo come UITransitionContextToViewKey
 LRTransitionPopController *toVc = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];  
 //Ottengo la vista del container
 UIView *containView = [transitionContext containerView];
 //Aggiungono entrambi al container. Attenzione all'ordine: la view del controller di destinazione deve essere aggiunta dopo.
 [containView addSubview:fromVc.view];
 [containView addSubview:toVc.view];  
 UIButton *button = fromVc.button;
 //Disegno un cerchio
 UIBezierPath *startPath = [UIBezierPath bezierPathWithOvalInRect:button.frame];
 //Creo due istanze di UIBezierPath; una con la dimensione del button e un'altra con un raggio sufficiente per coprire lo schermo. L'animazione finale si svolge tra questi due percorsi bezier.
 //Punto dell'angolo più lontano dal centro del pulsante rispetto allo schermo
 CGPoint finalPoint;
 //Determina in quale quadrante si trova il punto di attivazione
 if(button.frame.origin.x > (toVc.view.bounds.size.width / 2)){
  if (button.frame.origin.y < (toVc.view.bounds.size.height / 2)) {
   //Primo quadrante
   finalPoint = CGPointMake(0, CGRectGetMaxY(toVc.view.frame));
  altrimenti {
   //Quarto quadrante
   finalPoint = CGPointMake(0, 0);
  }
 altrimenti {
  if (button.frame.origin.y < (toVc.view.bounds.size.height / 2)) {
   //Secondo quadrante
   finalPoint = CGPointMake(CGRectGetMaxX(toVc.view.frame), CGRectGetMaxY(toVc.view.frame));
  altrimenti {
   //Terzo quadrante
   finalPoint = CGPointMake(CGRectGetMaxX(toVc.view.frame), 0);
  }
 } 
 CGPoint startPoint = CGPointMake(button.center.x, button.center.y);
 //Calcola il raggio di espansione esterna = distanza dal centro del pulsante al lato più lontano dello schermo - raggio del pulsante
 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)];  
 //Assign to the mask of the toVc view layer
 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"]; 
}

In the method used to customize the transition animation within the controller, return the custom animation class just now.

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

Fino a questo punto, l'animazione di transizione personalizzata è completata

L'animazione di pop è solo il contrario dell'animazione di push, non verrà spiegato in dettaglio qui, chi ha domande può guardare il codice

Aggiungere lo swipe di ritorno

Come detto prima, questo metodo aggiunge l'interazione utente per questa animazione quindi dobbiamo implementare il ritorno dello swipe durante il pop

Il modo più semplice dovrebbe essere utilizzare la classe UIPercentDrivenInteractiveTransition fornita da UIKit, questa classe ha già implementato l'protocollo UIViewControllerInteractiveTransitioning, gli studenti possono utilizzare l'oggetto di questa classe per specificare la percentuale di completamento della transizione di scena.

//Aggiungere l'interazione utente per questa animazione
- (nullable id)navigationController:(UINavigationController *)navigationController       interactionControllerForAnimationController:(id) animationController NS_AVAILABLE_IOS(7_0);

Primo passo: aggiungere il gesto

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

Secondo passo: determinare la proporzione dell'animazione in esecuzione attraverso le variazioni dello swipe dell'utente

- (void)handlePan:(UIPanGestureRecognizer *)gestureRecognizer {  
 /*Chiamare il metodo updateInteractiveTransition: dell'UIPercentDrivenInteractiveTransition può controllare fino a dove l'animazione di transizione è in corso,
  Quando il gesto di scorrimento dell'utente è completato, chiamare finishInteractiveTransition o cancelInteractiveTransition, UIKit eseguirà automaticamente la metà rimanente dell'animazione,
  o tornare l'animazione all'inizio.*/   
 if ([gestureRecognizer translationInView:self.view].x>=0) {
  //La proporzioni dello swipe
  CGFloat per = [gestureRecognizer translationInView:self.view].x / (self.view.bounds.size.width)
  per = MIN(1.0, (MAX(0.0, per)));   
  se (gestureRecognizer.state == UIGestureRecognizerStateBegan) {    
   self.interactiveTransition = [UIPercentDrivenInteractiveTransition new];
   [self.navigationController popViewControllerAnimated:YES];    
  altrimenti se (gestureRecognizer.state == UIGestureRecognizerStateChanged) {    
   se ([gestureRecognizer translationInView:self.view].x == 0) {     
    [self.interactiveTransition updateInteractiveTransition:0.01];     
   altrimenti {     
    [self.interactiveTransition updateInteractiveTransition:per];
   }    
  altrimenti se (gestureRecognizer.state == UIGestureRecognizerStateEnded || gestureRecognizer.state == UIGestureRecognizerStateCancelled) {    
   se ([gestureRecognizer translationInView:self.view].x == 0) {     
    [self.interactiveTransition cancelInteractiveTransition];
    self.interactiveTransition = nil;     
   altrimenti se (per > 0.5) {     
    [ self.interactiveTransition finishInteractiveTransition];
   altrimenti {
    [ self.interactiveTransition cancelInteractiveTransition];
   }
   self.interactiveTransition = nil;
  }     
 altrimenti se (gestureRecognizer.state == UIGestureRecognizerStateChanged) {
  [self.interactiveTransition updateInteractiveTransition:0.01];
  [self.interactiveTransition cancelInteractiveTransition]; 
 } else if ((gestureRecognizer.state == UIGestureRecognizerStateEnded || gestureRecognizer.state == UIGestureRecognizerStateCancelled)){   
  self.interactiveTransition = nil;
 }  
}

Passo terzo: nel metodo dell'agente dell'interazione utente per l'animazione, restituisci un'istanza di UIPercentDrivenInteractiveTransition

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

Se senti che questo articolo ti è stato utile, premi il pulsante Mi piace, grazie!

Il codice è stato messo inGitHubSul sito web è possibile scaricare, naturalmente, è possibile passare attraversoDownload locale

Sommario

Questo è tutto il contenuto dell'articolo, spero che il contenuto di questo articolo abbia un valore di riferimento per lo studio o il lavoro di tutti, se avete domande, potete lasciare un messaggio di commento, grazie per il supporto del Corso di urla.

Dichiarazione: il contenuto di questo articolo è stato tratto da Internet, il copyright spetta agli autori, il contenuto è stato contribuito volontariamente dagli utenti di Internet e caricato autonomamente, questo sito non detiene i diritti di proprietà, non è stato editato manualmente e non assume alcuna responsabilità legale correlata. Se trovi contenuti sospetti di copyright, invia un'e-mail a: notice#oldtoolbag.com (al momento dell'invio dell'e-mail, sostituisci # con @) per segnalare, fornendo prove pertinenti. Una volta verificata, questo sito eliminerà immediatamente il contenuto sospetto di violazione del copyright.

Ti potrebbe interessare