41

iPhoneのビルドインフォトアプリで画像間を閲覧するのと同じように、サブビューを画面の内外に移動できるようにしたいので、指で離したときにサブビューが画面から1/2以上離れている場合は、画面外をアニメートしますが、スワイプもサポートしている必要があるため、スワイプ/パンの速度が十分に速い場合は、画面外が1/2未満の場合でも、画面外をアニメートする必要があります。

私のアイデアは、UIPanGestureRecognizerを使用して、速度をテストすることでした。これは機能しますが、ビューの現在の位置とパンの速度に基づいてUIViewを移動するための正しいアニメーション期間を設定して、スムーズに見えるようにするにはどうすればよいですか?固定値を設定すると、指のスワイプ速度に比べてアニメーションが遅くなったり速くなったりします。

4

3 に答える 3

59

ドキュメントは言う

1秒あたりのポイントで表されるパンジェスチャの速度。速度は水平成分と垂直成分に分けられます。

つまり、ビュー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

于 2011-11-15T18:01:47.860 に答える
22

速度に依存することに関する観察UIPanGestureRecognizer:あなたの経験についてはわかりませんが、シミュレータでシステムが生成した速度はそれほど有用ではないことがわかりました。(デバイスでは問題ありませんが、シミュレーターでは問題があります。)

すばやくパンして突然停止し、待機してからジェスチャーを終了すると(たとえば、ユーザーがスワイプを開始し、これが目的ではないことに気付いたため、停止してから指を離す)、速度はで報告さvelocityInView:れますUIGestureRecognizerStateEnded指を離したときの状態は、停止して待つ前の速い速度のようですが、この例の正しい速度はゼロ(またはゼロに近い)になります。要するに、報告された速度は、鍋の終わりの直前の速度ですが、鍋自体の終わりの速度ではありません。

手動で速度を計算することになりました。(これが必要なのはばかげているようですが、本当にパンの最終速度を取得したいのであれば、それを回避する方法はわかりませんでした。)結論として、状態がUIGestureRecognizerStateChanged現在および以前のtranslationInViewCGPointを追跡しているとき時間だけでなく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];
    }
}
于 2012-05-02T16:33:05.683 に答える
3

ほとんどの場合、アニメーターUIViewAnimationOptionBeginFromCurrentStateのオプションを設定UIViewするだけで、完璧に継続するアニメーションを作成できます。

于 2014-08-31T16:25:40.030 に答える