Johnny Rockex Johnny Rockex - 19 days ago 6
iOS Question

CAShapeLayer Animation without having to update the path

When animating a CAShapeLayer, is it necessary to constantly keep updating the path of the layer, or is there a way to rely on CABasicAnimation alone?

In the example below, I set up four circles paths and draw them.
Then I want to animate them down to disappear. After the animation has completed however, they spring back to their original paths.

int radius = halfWidth-30; //the radius is the distance out from the centre
trackActive.path = [UIBezierPath bezierPathWithArcCenter:cicleCenter radius:radius startAngle:degreesToRadians(circleStart) endAngle:degreesToRadians(circleEnd) clockwise:true].CGPath;
circleActive.path = [UIBezierPath bezierPathWithArcCenter:cicleCenter radius:radius startAngle:degreesToRadians(circleStart) endAngle:degreesToRadians(circleEnd) clockwise:true].CGPath;

radius = halfWidth - 10;
trackNew.path = [UIBezierPath bezierPathWithArcCenter:cicleCenter radius:radius startAngle:degreesToRadians(circleStart) endAngle:degreesToRadians(circleEnd) clockwise:true].CGPath;
circleNew.path = [UIBezierPath bezierPathWithArcCenter:cicleCenter radius:radius startAngle:degreesToRadians(circleStart) endAngle:degreesToRadians(circleEnd) clockwise:true].CGPath;

NSArray * layers = @[trackActive, circleActive, trackNew, circleNew];
for (CAShapeLayer * layer in layers){

[CATransaction begin];
CABasicAnimation * animation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
animation.duration = 1.0f;
animation.removedOnCompletion = false;
animation.fromValue = @(1.0);
animation.toValue = @(0.0);
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
[layer addAnimation:animation forKey:@"circleAnimation"];

[CATransaction commit];
}


I understand that I can add this to set the new path:

[CATransaction setCompletionBlock:^{

//set the new path
layer.path = [UIBezierPath bezierPathWithArcCenter:cicleCenter radius:radius startAngle:degreesToRadians(circleStart) endAngle:degreesToRadians(arcEnd) clockwise:true].CGPath;

}];


But would prefer avoid keep having to update the path, and instead rely on the animation layer exclusively. Is this possible?

Something like: layer.path = animation.path or animation.updatesOriginalPath=true; could be wishful thinking, not sure.

Answer

You can simplify your code and omit the spring effect:

[CATransaction begin];
[CATransaction setAnimationDuration:1.0];
[CATransaction setAnimationTimingFunction: kCAMediaTimingFunctionEaseInEaseOut];
layer.strokeEnd = 0.0;
[CATransaction commit];

The transaction will create and add an implicit animation object for you.