slider
slider

Reputation: 2816

Is CABasicAnimation's toValue relative to initial position?

I have a CALayer that I'm explicitly animating to a new position. My toValue property seems only relative to the layer's initial position.

    CABasicAnimation *move = [CABasicAnimation animationWithKeyPath:@"position"];
    move.fromValue = [NSValue valueWithCGPoint:CGPointMake(blueDot.position.x, blueDot.position.y)];
    move.toValue = [NSValue valueWithCGPoint:CGPointMake(self.view.center.x, self.view.center.y)];
    move.duration = 1.0;
    [blueDot addAnimation:move forKey:@"myMoveAnimation"];
    blueDot.position = CGPointMake(self.view.center.x, self.view.center.y);

So this code isn't redrawing the layer to the view's center. It seems as if it's really redrawing the layer to CGPointMake(self.view.center.x + blueDot.position.x, self.view.center.y + blueDot.position.y), like it's adding the float values of self.view.center to the layer's starting position.

Is the toValue property supposed to act like this? What's the cleanest way to move my blueDot to the center of the view?


EDIT:

    -(void)viewDidLoad {
        [super viewDidLoad];

        blueDot = [CAShapeLayer layer];
        [blueDot setPath:[[UIBezierPath bezierPathWithOvalInRect:CGRectMake(self.view.center.x - 60 - 5, 163.5, 10, 10)] CGPath]];
        [blueDot setStrokeColor: [UIColor blueColor]CGColor];
        [blueDot setFillColor:[UIColor blueColor]CGColor];
        [[self.view layer] addSublayer:blueDot];
    }

// 163.5 is a placeholder for another view's position value so it's more readable for you guys.

Upvotes: 1

Views: 747

Answers (2)

Rob
Rob

Reputation: 437917

The issue is that you're moving the frame of the layer, but the blue dot path of this CAShapeLayer has been offset within that layer's frame by self.view.center.x - 60 - 5, 163.5, the coordinates you specified when creating the path for that CAShapeLayer. So when you animate the position of the layer, the blue dot will still be offset by that amount from this new position.

The fix is to define the path of the CAShapeLayer to be in the upper left corner of the layer, e.g.

CAShapeLayer *blueDot = [CAShapeLayer layer];
blueDot.path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(5, 5) radius:5 startAngle:0 endAngle:M_PI * 2 clockwise:true].CGPath;  // note that using arc often generates a better looking circle, probably not noticeable at this size, just probably good practice
blueDot.fillColor = [UIColor blueColor].CGColor;

Now when you move the position, that's where the blue dot's origin will move.


As an aside, I'd generally create a separate UIView and add this CAShapeLayer to that. This way, you can enjoy a variety of different features:

  • you can enjoy UIView block-based animation;
  • you can use center to move it around so that when you move its center to the center of its superview, it will really be centered (right now, if you move the position of the layer to self.view.center, it's not really quite centered, as it will be the upper left corner of the blue dot's layer that will be in the center);
  • you can use UIKit Dynamics to snap it to location or define other behaviors,
  • you can use auto-layout constraints to dictate placement if you want,
  • you could define a UIView subclass that is IBDesignable with this dot as a view so that you could add it on a storyboard and see what it looks like; etc.

Upvotes: 6

sgl0v
sgl0v

Reputation: 1417

You set the toValue in the coordinates system of the superview of view (because of using center property). Looks like this should work:

CABasicAnimation *move = [CABasicAnimation animationWithKeyPath:@"position"];
move.fromValue = [NSValue valueWithCGPoint:CGPointMake(blueDot.position.x, blueDot.position.y)];
move.toValue = [NSValue valueWithCGPoint:CGPointMake(CGRectGetMidX(view.bounds), CGRectGetMidY(view.bounds))];
move.duration = 1.0;
[blueDot addAnimation:move forKey:@"myMoveAnimation"];
blueDot.position = CGPointMake(CGRectGetMidX(view.bounds), CGRectGetMidY(view.bounds));

Upvotes: -1

Related Questions