2013-10-06 121 views
10

更新:儘管我仍然想解決這個問題,我最終切換到animateWithDuration:delay:options:animations:completion:,它的工作更好。它缺少彈簧所提供的那種「反彈」,但至少它是可控的。UIView動畫與UIPanGestureRecognizer速度方式太快(不減速)


我想創建iOS版漂亮的手勢驅動的用戶界面,但我遇到了一些困難,得到的值,以產生一個很好的自然的感覺應用程序。

我使用animateWithDuration:delay:usingSpringWithDamping:initialSpringVelocity:options:animations:completion:,因爲我喜歡彈簧彈簧動畫。我正在使用手勢識別器在完成狀態下給出的速度初始化參數velocity。問題在於,如果我足夠快地移動並放手,速度就會達到數千,而我的觀點最終會從屏幕飛出,然後以令人頭暈目眩的復仇來回跳動。

我甚至會根據視圖需要移動的距離來調整動畫的持續時間,這樣如果只需要幾個像素,動畫將花費更少的時間。但是,這並沒有解決問題。它仍然結束了堅果。

什麼發生的是認爲應該在任何速度的用戶在拖動開始了,但在到達目標點時,應迅速減速,僅反彈在最後一點點(因爲它如果速度合理)。

我不知道我是否正確使用這種方法或值。這裏有一些代碼來展示我在做什麼。任何幫助,將不勝感激!

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

回答

12

在查找相關問題的解決方案時遇到此文章。問題是,你傳遞從UIPanGestureRecognizer速度,這是在點/秒,當- animateWithDuration:delay:usingSpringWithDamping:initialSpringVelocity:options:animations:completion希望......稍微更古怪的值:

彈簧的初始速度。爲了順利開始動畫,請將該值與視圖的速度匹配,因爲它與附件之前相同。 值1對應於在一秒內遍歷的總動畫距離。例如,如果總動畫距離爲200點,並且您希望動畫的開頭匹配100pt/s的視圖速度,則使用值0.5。

動畫方法希望速度以每秒「距離」爲單位,而不是每秒的點數。所以,你應該傳遞的值是(來自手勢識別器的速度)/(動畫期間行進的總距離)。這就是說,這正是我正在做的事情,當手勢識別器正在移動手勢識別器和動畫拾取時,它們之間仍然存在輕微但明顯的「呃逆」。也就是說,它應該比以前更好。

+0

嘿,你有沒有想過如何正確使用初始彈簧速度? – bogardon

+0

不!結束與一幀呃逆航運... – Arclite

2

首先,您需要計算動畫必須處理的剩餘距離。當你有增量的距離就可以進行計算這樣的速度:

CGFloat springVelocity = fabs(gestureRecognizerVelocity/distanceToAnimate); 

對於必須使用UIViewAnimationOptionCurveLinear乾淨的速度傳輸。

0

我有一個稍微不同的需求,但我的代碼可能會幫助,即基於(平移速度/平移轉換)的速度計算。

有點上下文:我需要在UIView的一側使用panGestureRecognizer來調整它的大小。

  • 如果iPad處於縱向模式時,該視圖連接左側,底部 和右側,和我拖累視圖的上邊框以調整 它。
  • 如果iPad處於橫向模式,則該視圖將附加到左側,頂部和底部的 ,然後拖動右側邊框以調整其大小。

Landscape

Portrait

這是我在IBAction爲爲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) 
} 

希望有所幫助。