3

I'm using a UIPanGestureRecognizer and UIAttachmentBehavior to move a UIView around the screen. When the user ends the gesture I apply the velocity of the gesture recognizer to the view using a UIDynamicItemBehavior and the addLinearVelocity:forItem: method.

Here is the code I use:

- (void)_handlePanGestureRecognized: (UIPanGestureRecognizer *)panGestureRecognizer
{
    if (panGestureRecognizer.state == UIGestureRecognizerStateBegan)
    {
        _attachmentBehavior.anchorPoint = panGestureRecognizer.view.center;
        [_dynamicAnimator addBehavior: _attachmentBehavior];
    }
    else if (panGestureRecognizer.state == UIGestureRecognizerStateChanged)
    {
        CGPoint point = [panGestureRecognizer locationInView: panGestureRecognizer.view.superview];
        _attachmentBehavior.anchorPoint = point;
    }
    else if (panGestureRecognizer.state == UIGestureRecognizerStateEnded)
    {
        [_dynamicAnimator removeBehavior: _attachmentBehavior];

        CGPoint velocity = [panGestureRecognizer velocityInView: panGestureRecognizer.view.superview];
        [_dynamicItemBehavior addLinearVelocity: velocity 
            forItem: self];
    }
}

When the view stops moving I would then like to have it snap to the closest edge of the screen but I currently have no way of knowing when it has stopped moving short of polling the view's center with a CADisplayLink.

Reid Main
  • 3,394
  • 3
  • 25
  • 42

1 Answers1

1

Have you tried attaching a UIDynamicAnimatorDelegate to your animator, and using the dynamicAnimatorDidPause: method to trigger snapping to the closest edge?

From reading on the developer forums, it sounds like some have had problems with their views staying in motion for a very long time (jiggling back and forth by 1 pixel, for example), but perhaps this will work for your case.

Greg
  • 33,450
  • 15
  • 93
  • 100
  • I have tried that. It does work but that callback happens a good second or two after the item stops moving. I realize that the physics engine behind the scenes may still be doing calculations but I'm pretty sure if I logged the center of the view using a CADisplayLink it would not change a a couple seconds before dynamicAnimatorDidPause: is called. – Reid Main Oct 04 '13 at 14:24
  • Yeah, I think you're right; periodically checking velocityForItem: on the UIDynamicItemBehavior to see if it has dropped below a threshold will probably be your best bet. – Greg Oct 04 '13 at 14:29
  • The annoying thing is that Apple did exactly what I want in the very first UIKit Dynamics demo they have at WWDC 2013. Five minutes into session 206 they throw pictures around the screen and if they land near a box they snap to it. They say it is a WWDC demo app but they never released the source code. – Reid Main Oct 04 '13 at 14:39
  • I'm starting to think that the solution is to tap into the action property on the UIDynamicItemBehavior and adjust the pushDirection of a UIPushBehavior so it is always pushing to the closest edge of the collision bounds. – Reid Main Oct 04 '13 at 16:43