31

我希望能夠在屏幕上和屏幕之外移動子視圖,就像在iPhone的照片應用內建立圖像一樣,所以如果子視圖超過1/2屏幕當我用手指放開它時,必須在屏幕上進行動畫製作,但它也必須支持滑動,因此如果滑動/平移速度足夠高,它必須在屏幕上進行動畫製作,即使屏幕可能小於1/2。基於UIPanGestureRecognizer速度的UIView動畫

我的想法是使用UIPanGestureRecognizer,然後測試速度。這是有效的,但是如何根據視圖的當前位置和平移速度爲移動UIView設置正確的動畫持續時間,以使其看起來平滑?如果我設置了一個固定的值,那麼與我的手指滑動速度相比,動畫開始減慢或變快。

回答

47

文檔說

的平移姿勢,這是在每秒點數表示的速度。速度被分解成水平和垂直分量。

所以我說,給你想要移動視圖xPoints(PT中測量),讓它熄滅屏幕,你可以計算出持續時間是運動的,像這樣:

CGFloat xPoints = 320.0; 
CGFloat velocityX = [panRecognizer velocityInView:aView].x; 
NSTimeInterval duration = xPoints/velocityX; 
CGPoint offScreenCenter = moveView.center; 
offScreenCenter.x += xPoints; 
[UIView animateWithDuration:duration animations:^{ 
    moveView.center = offScreenCenter; 
}]; 

您可能想要使用+ (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion來嘗試不同的UIViewAnimationOptions

20

依賴於速度的觀察UIPanGestureRecognizer:我不知道你的經驗,但我發現系統在模擬器上生成的速度並不是非常有用。 (在設備上沒問題,但在模擬器上有問題。)

如果你迅速平移並突然停止,等待,然後才結束手勢(例如,用戶開始滑動,意識到這不是他們想要的,所以他們停下來然後鬆開他們的手指),手指被釋放時velocityInView:處的狀態報告的速度似乎是我停下來等待之前的快速速度,而本示例中的正確速度爲零(或接近零)。簡而言之,報道的速度是在鍋底之前的速度,而不是鍋底的速度。

我最終手動計算了自己的速度。 (這似乎很愚蠢,但這是必要的,但我沒有看到任何方式,如果我真的想得到最終的平底鍋速度。)底線,當狀態是UIGestureRecognizerStateChanged我跟蹤當前和以前的translationInView CGPoint以及時間,然後在UIGestureRecognizerStateEnded中使用這些值來計算實際的最終速度。它工作得很好。

這裏是我計算速度的代碼。我碰巧沒有使用速度來計算動畫的速度,而是我正在使用它來確定用戶是否足夠快地移動或者足夠快地移動以使視圖移動超過屏幕一半以上觸發視圖之間的動畫,但計算最終速度的概念似乎適用於這個問題。下面的代碼:

- (void)handlePanGesture:(UIPanGestureRecognizer *)gesture 
{ 
    static CGPoint lastTranslate; // the last value 
    static CGPoint prevTranslate; // the value before that one 
    static NSTimeInterval lastTime; 
    static NSTimeInterval prevTime; 

    CGPoint translate = [gesture translationInView:self.view]; 

    if (gesture.state == UIGestureRecognizerStateBegan) 
    { 
     lastTime = [NSDate timeIntervalSinceReferenceDate]; 
     lastTranslate = translate; 
     prevTime = lastTime; 
     prevTranslate = lastTranslate; 
    } 
    else if (gesture.state == UIGestureRecognizerStateChanged) 
    { 
     prevTime = lastTime; 
     prevTranslate = lastTranslate; 
     lastTime = [NSDate timeIntervalSinceReferenceDate]; 
     lastTranslate = translate; 

     [self moveSubviewsBy:translate]; 
    } 
    else if (gesture.state == UIGestureRecognizerStateEnded) 
    { 
     CGPoint swipeVelocity = CGPointZero; 

     NSTimeInterval seconds = [NSDate timeIntervalSinceReferenceDate] - prevTime; 
     if (seconds) 
     { 
      swipeVelocity = CGPointMake((translate.x - prevTranslate.x)/seconds, (translate.y - prevTranslate.y)/seconds); 
     } 

     float inertiaSeconds = 1.0; // let's calculate where that flick would take us this far in the future 
     CGPoint final = CGPointMake(translate.x + swipeVelocity.x * inertiaSeconds, translate.y + swipeVelocity.y * inertiaSeconds); 

     [self animateSubviewsUsing:final]; 
    } 
} 
+0

它不適用於iOS 6,因爲最終翻譯和lastTranslation在'UIGestureRecognizerStateEnded'中是一樣的,因此最終速度始終爲0. – an0

+1

謝謝,我明白了。這是一個模擬器的問題。我向蘋果公司發佈了一個錯誤報告。 – an0

+0

我修改了源碼(更改了一些變量名稱),希望避免這種混淆。此代碼正常工作,但希望新的變量名稱使它更容易混淆。感謝您向Apple報告錯誤。 – Rob

1

在大多數情況下,設置UIViewAnimationOptionBeginFromCurrentState選項UIView動畫師就足以使一個完美的動畫繼續。