2013-10-06 4 views
10

Update: Obwohl ich das immer noch gerne lösen würde, landete ich auf animateWithDuration:delay:options:animations:completion: und es funktioniert viel schöner. Es fehlt dieser schöne "Sprung" am Ende, den der Frühling bietet, aber zumindest ist es kontrollierbar.UIView Animation mit UIPanGestureRecognizer velocity viel zu schnell (nicht bremsend)


Ich versuche, eine nette Geste gesteuerte Benutzeroberfläche für iOS zu schaffen, aber in einige Schwierigkeiten leite die Werte immer in einem schönen natürlichen Gefühl App führen.

Ich benutze , weil ich die federnd Federanimation mag. Ich initialisiere das velocity Argument mit der Geschwindigkeit, die durch den Gestenerkenner im vollendeten Zustand gegeben wird. Das Problem ist, wenn ich schnell genug schwenke und loslasse, ist die Geschwindigkeit in den Tausenden, und meine Ansicht fliegt vom Bildschirm und hüpft dann mit schwindelerregender Rache hin und her.

Ich justiere sogar die Dauer der Animation relativ zu der Distanz, um die sich die Ansicht bewegen muss, so dass die Animation weniger Zeit benötigt, wenn nur ein paar Pixel benötigt werden. Das hat das Problem jedoch nicht gelöst. Es endet immer noch verrückt.

Was ich möchte passieren soll, sollte die Ansicht bei welcher Geschwindigkeit der Benutzer es ziehen soll, aber es sollte schnell beim Erreichen des Zielpunktes abbremsen und nur ein wenig am Ende abprallen (wie es tut wenn die Geschwindigkeit etwas Vernünftiges ist).

Ich frage mich, ob ich diese Methode oder die Werte richtig verwende. Hier ist ein Code, um zu zeigen, was ich mache. Jede Hilfe wäre willkommen!

- (void)handlePanGesture:(UIPanGestureRecognizer*)gesture { 
    CGPoint offset = [gesture translationInView:self.view]; 
    CGPoint velocity = [gesture velocityInView:self.view]; 

    NSLog(@"pan gesture state: %d, offset: %f velocity: %f", gesture.state, offset.x, velocity.x); 
    static CGFloat initialX = 0; 

    switch (gesture.state) { 
     case UIGestureRecognizerStateBegan: { 
      initialX = self.blurView.x; 
     break; } 

     case UIGestureRecognizerStateChanged: { 
      self.blurView.x = initialX + offset.x; 
     break; } 

     default: 
     case UIGestureRecognizerStateCancelled: 
     case UIGestureRecognizerStateEnded: { 
      if (velocity.x > 0) 
       [self openMenuWithVelocity:velocity.x]; 
      else 
       [self closeMenuWithVelocity:velocity.x]; 
     break; } 
    } 
} 

- (void)openMenuWithVelocity:(CGFloat)velocity { 
    if (velocity < 0) 
     velocity = 1.5f; 

    CGFloat distance = -40 - self.blurView.x; 
    CGFloat distanceRatio = distance/260; 
    NSLog(@"distance: %f ratio: %f", distance, distanceRatio); 

    [UIView animateWithDuration:(0.9f * distanceRatio) delay:0 usingSpringWithDamping:0.7 initialSpringVelocity:velocity options:UIViewAnimationOptionBeginFromCurrentState animations:^{ 
     self.blurView.x = -40; 
    } completion:^(BOOL finished) { 
     self.isMenuOpen = YES; 
    }]; 
} 

Antwort

12

Kam über diesen Beitrag während der Suche nach einer Lösung für ein verwandtes Problem. Das Problem ist, sind Sie in der Geschwindigkeit von UIPanGestureRecognizer vorbei, die/Sekunde in Punkten, wenn - animateWithDuration:delay:usingSpringWithDamping:initialSpringVelocity:options:animations:completion wollen ... einen etwas odder Wert:

Die anfängliche Federgeschwindigkeit. Um einen reibungslosen Start der Animation zu gewährleisten, vergleichen Sie diesen Wert mit der Geschwindigkeit der Ansicht, wie sie vor dem Anhängen war. Der Wert 1 entspricht der gesamten in einer Sekunde zurückgelegten Animationsentfernung. Wenn die gesamte Animationsentfernung beispielsweise 200 Punkte beträgt und Sie möchten, dass der Anfang der Animation mit einer Ansichtsgeschwindigkeit von 100 pt/s übereinstimmt, verwenden Sie einen Wert von 0,5.

Die Animationsmethode will Geschwindigkeit in "Distanzen" pro Sekunde, nicht Punkte pro Sekunde. Der Wert, den Sie übergeben sollten, ist also (Geschwindigkeit von der Gestenerkennung)/(die gesamte während der Animation zurückgelegte Entfernung).

Das ist genau das, was ich mache, und es gibt immer noch einen leichten, aber wahrnehmbaren "Schluckauf" zwischen der Bewegung der Gestenerkennung und der Aufnahme der Animation. Das heißt, es sollte noch viel besser funktionieren als das, was Sie vorher hatten.

+0

hey hast du jemals herausgefunden, wie man die anfängliche Federgeschwindigkeit richtig benutzt? – bogardon

+0

Nein! Endete Versand mit dem Ein-Rahmen-Schluckauf ... – Arclite

2

Zuerst müssen Sie die verbleibende Entfernung berechnen, um die sich die Animation kümmern muss. Wenn Sie die Delta-Abstand haben, können Sie fortfahren, die Geschwindigkeit wie folgt zu berechnen:

CGFloat springVelocity = fabs(gestureRecognizerVelocity/distanceToAnimate); 

Für saubere Geschwindigkeitsübertragung Sie UIViewAnimationOptionCurveLinear verwenden.

0

Ich hatte ein etwas anderes Bedürfnis, aber mein Code kann helfen, nämlich die Geschwindigkeitsberechnung, basierend auf (Schwenkgeschwindigkeit/Schwenkübersetzung).

Ein bisschen Kontext: Ich brauchte einen panGestureRecognizer auf der Seite eines UIView, um die Größe zu ändern.

  • Wenn das iPad im Hochformat wird die Ansicht auf der linken Seite angebracht, unten und die rechten Seite, und ich ziehe an der oberen Grenze der Ansicht es zu ändern.
  • Wenn das iPad im Querformatmodus ist, wird die Ansicht an die linke, obere und untere Seite der angehängt, und ich ziehe am rechten Rand, um die Größe zu ändern.

Landscape

Portrait

Dies ist, was ich in der IBAction für die UIPanGestureRecognizer verwendet:

var velocity: CGFloat = 1 

    switch gesture.state { 
     case .changed: 
     // Adjust the resizableView size according to the gesture translation 
     let translation = gesture.translation(in: resizableView) 
     let panVelocity = gesture.velocity(in: resizableView) 

     if isPortrait { // defined previously in the class based on UIDevice orientation 

      let newHeight = resizableViewHeightConstraint.constant + (-translation.y) 
      resizableViewHeightConstraint.constant = newHeight 

      // UIView animation initialSpringVelocity requires a velocity based on the total distance traveled during the animation 
      velocity = -panVelocity.y/-panTranslation.y 

     } else { // Landscape 
      let newWidth = resizableViewWidthConstraint.constant + (translation.x) 

      // Limit the resizing to half the width on the left and the full width on the right 
      resizableViewWidthConstraint.constant = min(max(resizableViewInitialSize, newWidth), self.view.bounds.width) 

      // UIView animation initialSpringVelocity requires a velocity based on the total distance traveled during the animation 
      velocity = panVelocity.x/panTranslation.x 
     } 

     UIView.animate(withDuration: 0.5, 
         delay: 0, 
         usingSpringWithDamping: 1, 
         initialSpringVelocity: velocity, 
         options: [.curveEaseInOut], 
         animations: { 
         self.view.layoutIfNeeded() 
         }, 
         completion: nil) 

     // Reset translation 
     gesture.setTranslation(CGPoint.zero, in: resizableView) 
} 

Hoffnung, das hilft.