Why does my view jump when setting the layer anchorPoint in an animation block?
Asked Answered
V

1

2

I have a UIPanGestureRecognizer attached to a view in my iOS app. I copied the code from the Touches sample app for the pan handler. When the gesture starts, my code:

  • Records the original anchor point and center.
  • Changes the anchor point and center to be around the user's fingers, like so:

    CGPoint locationInView = [gestureRecognizer locationInView:target];
    CGPoint locationInSuperview = [gestureRecognizer locationInView:target.superview];
    target.layer.anchorPoint = CGPointMake(
        locationInView.x / target.bounds.size.width,
        locationInView.y / target.bounds.size.height
    );
    target.center = locationInSuperview;
    

As the gesture progresses, the pan handler continuously changes the center to track the finger movement. So far so good.

When the user lets go, I want the view to animate back to its original starting point. The code that does that looks like this:

[UIView animateWithDuration:2 delay:0 options:UIViewAnimationCurveEaseOut animations:^{
    target.center            = originalCenter;
    target.layer.anchorPoint = originalAnchorPoint;
}];

And that does animate the view back to its original starting point. However, before the animation starts, the view jumps to a different spot in the UI. IOW, let go, it jumps, then it animates back to where it belongs.

I thought perhaps I needed to set the anchor point and center outside the animation, perhaps setting the center to the location in the super view, like when the gesture begins, but that seems to make no difference.

What am I missing here? How can I prevent the jump when the user lets go?

Verdugo answered 6/5, 2012 at 4:57 Comment(1)
make sure you understand what is anchorPoint: https://mcmap.net/q/23484/-changing-my-calayer-39-s-anchorpoint-moves-the-viewLodge
P
11

I suspect two issues without trying out what you are doing:

Changing the anchor point changes a view's/layer's position. In order to change an anchor point without modification of the position, you can use some helper like this one:

-(void)setAnchorPoint:(CGPoint)anchorPoint forView:(UIView *)view
{
    CGPoint newPoint = CGPointMake(view.bounds.size.width * anchorPoint.x, view.bounds.size.height * anchorPoint.y);
    CGPoint oldPoint = CGPointMake(view.bounds.size.width * view.layer.anchorPoint.x, view.bounds.size.height * view.layer.anchorPoint.y);

    newPoint = CGPointApplyAffineTransform(newPoint, view.transform);
    oldPoint = CGPointApplyAffineTransform(oldPoint, view.transform);

    CGPoint position = view.layer.position;

    position.x -= oldPoint.x;
    position.x += newPoint.x;

    position.y -= oldPoint.y;
    position.y += newPoint.y;

    view.layer.position = position;
    view.layer.anchorPoint = anchorPoint;
}

(I'm using that myself in my projects. Found here: Changing my CALayer's anchorPoint moves the view)

Your animation sets the anchor point back to its original value. You should use the helper above to reset the anchor point. This ensures that the view won't move when changing the anchor. You'll have to do this outside of the animation. Afterwards, use an animation block to change the view's center and animate it to where you want it to be.

Phia answered 22/5, 2012 at 11:2 Comment(3)
I had tried this before, but maybe I didn't get it right. Will try again tonight. Thanks!Verdugo
Well, that makes it jump to a completely different part of the screen, but it still jumps.:-( Trying to determine if it somehow has something to do with the order in which I set things: the view's center, anchorPoint, and transform.Verdugo
And the answer is “yes!” I was setting the center, then the anchorPoint, then the transform. I have changed it to set the anchorPoint (using the helper), then the transform, and finally the center, and now it works exactly right. Phew! Thanks for the reminder about this function, I just needed to fiddle with it a bit more.Verdugo

© 2022 - 2024 — McMap. All rights reserved.