UIView animation based on UIPanGestureRecognizer velocity UIView animation based on UIPanGestureRecognizer velocity ios ios

UIView animation based on UIPanGestureRecognizer velocity


The docs say

The velocity of the pan gesture, which is expressed in points per second. The velocity is broken into horizontal and vertical components.

So I'd say, given you want to move your view xPoints (measured in pt) to let it go off-screen, you could calculate the duration for that movement like so:

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

You might want to use + (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion instead though and try out different UIViewAnimationOptions.


An observation on relying upon the velocity in UIPanGestureRecognizer: I don't know about your experience, but I found the system generated velocity on the simulator to be not terribly useful. (It's ok on the device, but problematic on simulator.)

If you pan quickly across and abruptly stop, wait, and only then end the gesture (e.g. user starts a swipe, realizes that this wasn't what they wanted, so they stop and then release their finger), the velocity reported by velocityInView: at the UIGestureRecognizerStateEnded state when your finger was released seems to be the fast speed before I stopped and waited, whereas the right velocity in this example would be zero (or near zero). In short, the reported velocity is that which it was right before the end of the pan, but not the speed at the end of the pan itself.

I ended up calculating the velocity myself, manually. (It seems silly that this is necessary, but I didn't see any way around it if I really wanted to get the final speed of the pan.) Bottom line, when the state is UIGestureRecognizerStateChanged I keep track of the current and previous translationInView CGPoint as well as the time, and then using those values when I was in the UIGestureRecognizerStateEnded to calculate the actual final velocity. It works pretty well.

Here is my code for calculating the velocity. I happen to not be using velocity to figure out the speed of the animation, but rather I'm using it to determine whether the user either panned far enough or flicked quickly enough for the view to move more than half way across the screen and thus triggering the animation between views, but the concept of calculating the final velocity seems applicable to this question. Here's the code:

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


In most cases setting UIViewAnimationOptionBeginFromCurrentState option for UIView animator is enough to make a flawlessly continued animation.