1

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.

Johnny Rockex
  • 4,136
  • 3
  • 35
  • 55
  • 1
    You can omit the spring back effect by setting strokeEnd of the layer to zero, because the animation doesn't modify the layer's state. – clemens Nov 17 '16 at 18:18

2 Answers2

1

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.

clemens
  • 16,716
  • 11
  • 50
  • 65
-1

You can set a flag in a an animation so it stays where it was in the end and starts where it should start like this:

[animation setRemovedOnCompletion:NO];
[animation setFillMode:kCAFillModeBoth];

check also this answer: What exactly does removedOnCompletion = NO do?

Community
  • 1
  • 1
ben
  • 900
  • 9
  • 17
  • 2
    You should avoid keeping animations on layers. Removing or replacing the animation later will lead to _unexpected_ effects. A `layer.strokeEnd = 0.0` before `addAnimation` will have the same effect. – clemens Nov 18 '16 at 14:54
  • Another disadvantage of keeping the animation is that the layer doesn't reflects it's _visible state_. It returns always 1.0 for `strokeEnd` but it displays the value for 0.0. – clemens Nov 18 '16 at 15:04