0

Consider the following animation:

CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
pathAnimation.duration = 1.0;
pathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
pathAnimation.fromValue = [NSNumber numberWithFloat:0.0f];
pathAnimation.toValue = [NSNumber numberWithFloat:1.0f];
pathAnimation.removedOnCompletion = NO;
pathAnimation.delegate = self;

This will essentially animate the drawing of the layer from one end to the next. The problem is that once the animation completes, the strokeEnd property resets back to 0 (where it was initially set). How do I make the final value "stick"?

I have attempted to change this in the animationDidStop delegate method. This mostly works, but can cause a flash of strokeEnd at 0 briefly, even when put inside a CATransaction to disable animations. I have also played with the additive and cumulative properties to no avail. Any suggestions?

Mike
  • 1,112
  • 1
  • 13
  • 20
  • possible duplicate of [Animation of stroke of circle segment ends in complete stroke](http://stackoverflow.com/questions/9142888/animation-of-stroke-of-circle-segment-ends-in-complete-stroke) – Jano May 01 '13 at 21:42
  • I find it easier to set the properties on my actual layer, and then apply an animation that specifies the time and easing. In some cases I will specifically the starting values, but leaving the end values to be animated automatically solves this problem. – Marc May 02 '13 at 16:08

1 Answers1

10

You simply set the strokeEnd property to its final value yourself! Like this:

// your current code (stripped of unnecessary stuff)

CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
pathAnimation.duration = 1.0;
pathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
pathAnimation.fromValue = [NSNumber numberWithFloat:0.0f];
pathAnimation.toValue = [NSNumber numberWithFloat:1.0f];

// just add this to it:

theLayer.strokeEnd = 1;

// and now add the animation to theLayer

If strokeEnd were implicitly animatable (so that that line would itself cause animation) we would turn off implicit animation first:

[CATransaction setDisableActions:YES];
theLayer.strokeEnd = 1;

Note that your pathAnimation.removedOnCompletion = NO; is wrong, and so is the other answer's kCAFillModeForwards. Both are based on misunderstandings of how Core Animation works.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • Thank you for the clarification, as I wrote in a comment in my answer I always thought that setting the fillMode would cause the model layer to be updated on animation end. Just to know, when one should use fillMode and removedOnCompletion=NO ? I'm thinking about that but I cannot find an example. – Alessandro Orrù May 01 '13 at 22:05
  • They are useful in complex grouped animations. Again, see my book referred to above (search that chapter on `fillMode`). The point is that that trick is a massive misunderstanding that unfortunately got loose on the Internet. Even Apple had an example where they gave the wrong impression about this (Metronome, which has since been taken down). – matt May 01 '13 at 22:08
  • Thanks Matt. Within my animationDidStop delegate method, I am setting the `strokeEnd` back to 1. Here is my code snippet: `[CATransaction setDisableActions:YES]; self.theLayer.strokeEnd = 1.0; [CATransaction setDisableActions:NO];` However, I am still getting a flash, I assume because once the animation completes, it resets the strokeEnd back to the layer's original value (which is 0), and then setting it to 1.0. How do I remove this flash effect? – Mike May 02 '13 at 13:55
  • Just please try doing what I said. Not in the delegate. Right here as part of the code you already showed. – matt May 02 '13 at 15:22
  • I modified my answer to show you what I'm saying. Just set the thing to its final value at the same moment you are adding the animation to the layer. – matt May 02 '13 at 15:25
  • No, this doesn't work! If I add the line to set the property right below the CAAnimation configuration, then the change happens before the animation even starts to show. Sure, it solves the problem of ending at that value, but now it shows it before the animation has started, too. On a side note: Please don't push us to "see your book" to gain a deeper understanding. Just condense what's necessary to solve the problem into your response, or quote from the book, instead of forcing people to sift through a wall of text for the answer. – Erika Electra Mar 02 '18 at 10:48
  • @Cindeselia Happy to take out the "see your book" part; done. Obviously I don't agree that what I said didn't work, back in 2013, or I wouldn't have said it. If it doesn't work now, please do give a more current answer. – matt Mar 02 '18 at 20:35