2013-10-06 10 views
10

Aggiornamento: Anche se mi piace ancora di risolvere questo problema, ho finito per passare a animateWithDuration:delay:options:animations:completion: e funziona molto più bello. Manca quel bel "rimbalzo" alla fine che la molla offre, ma almeno è controllabile.UIView animazione con UIPanGestureRecognizer modo velocità troppo veloce (non in decelerazione)


Sto cercando di creare un bel gesto UI-driven per iOS, ma sono in esecuzione in alcune difficoltà che ottengono i valori di provocare una bella app sentimento naturale.

Sto usando animateWithDuration:delay:usingSpringWithDamping:initialSpringVelocity:options:animations:completion: perché mi piace l'animazione molla rimbalzante. Sto inizializzando l'argomento velocity con la velocità data dal riconoscitore di gesti nello stato completato. Il problema è che se faccio una panoramica abbastanza veloce e lascia andare, la velocità è a migliaia, e la mia vista finisce volando fuori dallo schermo e poi rimbalza avanti e indietro con una vendetta così vertiginosa.

Sto persino regolando la durata dell'animazione in relazione alla quantità di distanza che la vista deve spostare, in modo che se ci sono solo pochi pixel necessari, l'animazione impiegherà meno tempo. Ciò, tuttavia, non ha risolto il problema. Continua a diventare matto.

Quello che voglio accadere è la vista dovrebbe iniziare a qualsiasi velocità l'utente è trascinandolo a, ma dovrebbe decelerare rapidamente quando si raggiunge il punto di destinazione e rimbalzare solo un po 'alla fine (come fa se la velocità è qualcosa di ragionevole).

Mi chiedo se sto usando questo metodo oi valori correttamente. Ecco un codice per mostrare cosa sto facendo. Qualsiasi aiuto sarebbe apprezzato!

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

risposta

12

In questo post si è cercato una soluzione per un problema correlato. Il problema è, si sta passando nella velocità da UIPanGestureRecognizer, che si trova in punti/secondo, quando - animateWithDuration:delay:usingSpringWithDamping:initialSpringVelocity:options:animations:completion vuole ... un valore leggermente più strano:

la velocità iniziale della molla. Per iniziare bene l'animazione, abbinare questo valore alla velocità della vista come era prima dell'allegato. Il valore 1 corrisponde alla distanza di animazione totale percorsa in un secondo. Ad esempio, se la distanza di animazione totale è di 200 punti e si desidera che l'inizio dell'animazione corrisponda a una velocità di visualizzazione di 100 pt/s, utilizzare un valore di 0,5.

Il metodo di animazione richiede velocità in "distanze" al secondo, non in punti al secondo. Quindi, il valore che dovresti passare è (velocità dal riconoscimento del gesto)/(distanza totale percorsa durante l'animazione).

Detto questo, questo è esattamente quello che sto facendo e c'è ancora un leggero "intoppo" ma evidente tra quando il rilevatore di gesti lo sta spostando e quando l'animazione riprende. Detto questo, dovrebbe funzionare ancora molto meglio di quello che avevi prima.

+0

hey hai mai capito come utilizzare correttamente la velocità della molla iniziale? – bogardon

+0

No! Finito il trasporto con il singhiozzo di un frame ... – Arclite

2

Per prima cosa è necessario calcolare la distanza rimanente di cui l'animazione dovrà occuparsi. Quando si ha la distanza delta si può procedere per calcolare la velocità in questo modo:

CGFloat springVelocity = fabs(gestureRecognizerVelocity/distanceToAnimate); 

Per il trasferimento velocità pulita è necessario utilizzare UIViewAnimationOptionCurveLinear.

0

Avevo un bisogno leggermente diverso, ma il mio codice può essere d'aiuto, in particolare il calcolo della velocità, basato sulla (velocità di spostamento pan/traslazione).

Un po 'di contesto: avevo bisogno di usare un panGestureRecognizer sul lato di un UIView per ridimensionarlo.

  • Se l'iPad è in modalità verticale, la vista è attaccato a sinistra, e destra lati inferiori, e trascinare sul bordo superiore della vista per ridimensionare esso.
  • Se l'iPad è in modalità orizzontale, la vista è collegata al lato sinistro, superiore e inferiore di e trascino sul bordo destro per ridimensionarlo.

Landscape

Portrait

Questo è quello che ho usato nel IBAction per l'UIPanGestureRecognizer:

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) 
} 

Speranza che aiuta.

Problemi correlati