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

Wie man in iOS Controller-Transition-Animationen (push) anpassen kann

Einleitung

In letzter Zeit habe ich etwas Freizeit gehabt und habe meine kürzlich abgeschlossenen Projekte zusammengefasst. In diesem Artikel werde ich hauptsächlich über die benutzerdefinierten Controller-Übergangsanimationen von iOS push sprechen und sie teilen, um anderen zur Referenz und zum Lernen zur Verfügung zu stellen. Also werde ich nicht viel mehr sagen, lassen Sie uns gemeinsam einen detaillierten Überblick geben.

Detailliertes Bild:


iOS7 Apple hat die API für benutzerdefinierte Übergänge eingeführt. Seitdem können alle Animationen, die mit CoreAnimation implementiert werden können, zwischen zwei ViewController-Wechseln erscheinen. Und die Implementierung ist stark entkoppelt, was bedeutet, dass man, um andere Animationsschemata zu ersetzen, einfach einen Klassennamen ändern muss, was wirklich das Vergnügen eines hübschen Codes erfahren hat.

Es gibt viele Tutorials im Internet über eigen definierte Transitionseffekte, aber ich hoffe, dass die hier vorgestellten für alle leicht verständlich und einfach zu handhaben sind.

Es gibt zwei Arten von Transitionseffekten, Push und Modal, daher gibt es auch zwei Arten von eigen definierten Transitionseffekten. Heute sprechen wir über Push

eigen definierte Transitionseffekte Push

Zuerst das Interface aufbauen,4Tasten:

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

Animation hinzufügen:

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

Die Benutzeroberfläche ist aufgebaut:

Um eine personalisierte Übergangsanimation beim Pushen zu implementieren, muss das Protokoll UINavigationControllerDelegate befolgt werden.

Apple hat in UINavigationControllerDelegate mehrere Protokollmethoden bereitgestellt, deren spezifische Funktionen durch die Rückgabetypen klar erkennbar sind.

//um die Übergangsanimation zu personalisieren
- (nullable id)navigationController:(UINavigationController *)navigationController         animationControllerForOperation:(UINavigationControllerOperation)operation            fromViewController:(UIViewController *)fromVC             toViewController:(UIViewController *)toVC NS_AVAILABLE_IOS(7_0);
//Add user interaction to this animation
- (nullable id)navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController:(id) animationController NS_AVAILABLE_IOS(7_0);

Im ersten Ansatz muss ein Objekt, das das Protokoll UIViewControllerInteractiveTransitioning implementiert, zurückgegeben werden, und darin die Animation implementiert werden.

  • Erstellen Sie eine Klasse, die von NSObject abgeleitet ist und UIViewControllerAnimatedTransitioning deklariert.
  • Überladen Sie die Protokollmethode von UIViewControllerAnimatedTransitioning.
//Rückgabe der Animationsdauer
- (NSTimeInterval)transitionDuration:(nullable id)transitionContext;
//Schreiben Sie den Code der Animation darin
- (void)animateTransition:(id)transitionContext;

Zunächst erstelle ich eine benutzerdefinierte Klasse namens LRTransitionPushController, die von NSObject abgeleitet ist und das Protokoll UIViewControllerAnimatedTransitioning befolgt

 - (void)animateTransition:(id)transitionContext{  
 self.transitionContext = transitionContext;  
 //Erhalten Sie den Quellcontroller, beachten Sie nicht, UITransitionContextFromViewKey zu schreiben
 LRTransitionPushController *fromVc = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
 //Erhalten Sie den Zielcontroller, beachten Sie nicht, UITransitionContextToViewKey zu schreiben
 LRTransitionPopController *toVc = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];  
 //Erhalten Sie die Containeransicht
 UIView *containView = [transitionContext containerView];
 // Alle werden in den Container hinzugefügt. Beachten Sie die Reihenfolge, der View des Zielcontrollers muss nachträglich hinzugefügt werden.
 [containView addSubview:fromVc.view];
 [containView addSubview:toVc.view];  
 UIButton *button = fromVc.button;
 //Zeichnung des Kreises
 UIBezierPath *startPath = [UIBezierPath bezierPathWithOvalInRect:button.frame];
 //Erstellung von zwei圆形en UIBezierPath-Instanzen; einer für die Größe des Buttons und einer mit einem Radius, der ausreichend ist, um den Bildschirm zu bedecken. Die endgültige Animation erfolgt zwischen diesen Bezier-Pfaden.
 //Punkt der weitesten Ecke des Bildschirms vom Buttonzentrum
 CGPoint finalPoint;
 //Bestimmung des Quadrants des Auslösepunkts
 if(button.frame.origin.x > (toVc.view.bounds.size.width / 2)) {
  if (button.frame.origin.y < (toVc.view.bounds.size.height / 2)) {
   //Erstes Quadrant
   finalPoint = CGPointMake(0, CGRectGetMaxY(toVc.view.frame));
  }else{
   //Viertes Quadrant
   finalPoint = CGPointMake(0, 0);
  }
 }else{
  if (button.frame.origin.y < (toVc.view.bounds.size.height / 2)) {
   //Zweites Quadrant
   finalPoint = CGPointMake(CGRectGetMaxX(toVc.view.frame), CGRectGetMaxY(toVc.view.frame));
  }else{
   //Drittes Quadrant
   finalPoint = CGPointMake(CGRectGetMaxX(toVc.view.frame), 0);
  }
 } 
 CGPoint startPoint = CGPointMake(button.center.x, button.center.y);
 //Berechnung des Radius für die Ausbreitung vom Buttonzentrum zur weitesten Ecke des Bildschirms - Kontrollradius
 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 controller, use the method to return the custom transition animation class that was just customized

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

Up to this point, the custom transition animation is complete

The pop animation is just the reverse of the push animation, which will not be elaborated here. If you have any questions, please refer to the code

Add sliding return gesture

As mentioned above, this method is to add user interaction to this animation, so we need to implement the sliding return in the pop

The simplest way should be to use the UIPercentDrivenInteractiveTransition class provided by UIKit, which has already implemented the UIViewControllerInteractiveTransitioning protocol, students can use objects of this class to specify the completion percentage of the transition animation.

//Add user interaction to this animation
- (nullable id)navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController:(id) animationController NS_AVAILABLE_IOS(7_0);

First step: Add gesture

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

Second step: Determine the animation execution ratio through the change of user's sliding

- (void)handlePan:(UIPanGestureRecognizer *)gestureRecognizer {  
 /*By calling the updateInteractiveTransition: method of UIPercentDrivenInteractiveTransition, you can control to what extent the transition animation has progressed,
  When the user's pull-down gesture is completed, call finishInteractiveTransition or cancelInteractiveTransition, UIKit will automatically execute the remaining half of the animation,
  or let the animation return to its initial state.*/   
 if ([gestureRecognizer translationInView:self.view].x>=0) {
  //Gestures sliding ratio
  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;
 }  
}

Dritter Schritt: In der Methode, die als Proxy für die Benutzereingriffe bei der Animation zurückgegeben wird, die Instanz von UIPercentDrivenInteractiveTransition zurückgeben

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

Wenn Sie den Artikel nützlich finden, geben Sie bitte eine Geste des Likes, danke.

Der Code wurde hier abgelegtGitHubHier können Sie herunterladen, natürlich können Sie auch überLokale Downloads

Zusammenfassung

Das ist der gesamte Inhalt dieses Artikels. Wir hoffen, dass der Inhalt dieses Artikels für Ihre Lern- oder Arbeitsaktivitäten eine gewisse Referenz und Lernwerte hat. Wenn Sie Fragen haben, können Sie gerne Kommentare hinterlassen. Vielen Dank für Ihre Unterstützung für das Lernprogramm 'Schrei'.

Erklärung: Der Inhalt dieses Artikels wurde aus dem Internet übernommen und gehört dem Urheber. Der Inhalt wurde von Internetbenutzern freiwillig beigesteuert und hochgeladen. Diese Website besitzt keine Eigentumsrechte und hat den Inhalt nicht manuell bearbeitet. Sie übernimmt auch keine Haftung für relevante rechtliche Verantwortlichkeiten. Wenn Sie verdächtige urheberrechtliche Inhalte finden, freuen wir uns über eine E-Mail an: notice#oldtoolbag.com (bei der E-Mail sendung, ersetzen Sie bitte # durch @) zur Meldung von Missbrauch und geben Sie relevante Beweise an. Sobald nachgewiesen wird, dass eine Urheberrechtsverletzung vorliegt, wird diese Website die fraglichen Inhalte sofort löschen.

Empfohlene Artikel