web-dev-qa-db-ja.com

UIPanGestureRecognizerの速度に基づくUIViewアニメーション

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

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

36
Neigaard

ドキュメントは言う

パンジェスチャの速度。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を試してください。

54
Dennis

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

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

手動で速度を自分で計算することになりました。 (これが必要なのはばかげているように見えますが、パンの最終速度を本当に取得したい場合、私はそれを回避できませんでした。)ボトムライン、状態が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];
    }
}
21
Rob

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

3
nikans