I have a CAShapeLayer
in form of a square and I set the width
to be 2.0f.
I would like to animate its strokeEnd
and for that I use a CABasicAnimation
like so:
CABasicAnimation *stroke = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
stroke.fromValue = @(0);
stroke.toValue = @(1);
stroke.repeatCount = 1;
stroke.duration = 10.0f;
stroke.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
[myLayer addAnimation:stroke forKey:nil];
And it works. From the book by Matt Neuberg, I understand that we could omit the toValue
and fromValue
altogether by:
[CATransaction begin];
[CATransaction setDisableActions:YES];
..
..
[CATransaction commit];
His example was, however, based on transform
. And I am trying to achieve the same instead with strokeEnd
.
The best I can do is to set the strokeEnd
within CATransaction
block to either 1 or 0 and set the fromValue
correspondingly. Other words I don't seem to be able omit both toValue
and endValue
on animating strokeEnd
- I need at least one of them (i.e., fromValue
or toValue
).
So my question is, how could I be able to animate strokeEnd
without needing to use toValue
and fromValue
at all?
Thanks.
There are two very different issues at play here:
If you change one of your layer's animatable properties (i.e. not with a CABasicAnimation
, but rather changing the layer's properties in code, directly) Core Animation may apply implicit animations to the layer (a relatively fast animation, but an animation nonetheless). For example, if you change myLayer.opacity
, Core Animation may automatically apply implicit animations to fade this effect. Likewise if you directly change myLayer.strokeEnd
, it may animate the change of the stroke end (you'll see it quickly drawing the line, rather than just immediately appearing).
By using [CATransaction setDisableActions:YES]
, you can instruct Core Animation to not perform these implicit animations that may apply when you change the layer's properties directly. By using [CATransaction setDisableActions:NO]
, you will re-enable the implicit animations.
When you create a CABasicAnimation
, there are fromValue
, toValue
, and byValue
properties (which according to the documentation, "All are optional, and no more than two should be non-nil
."). Frequently you'll see animations that specify both the fromValue
and toValue
. But you want, you can specify one, and Core Animation will determine what the others should be.
For example, let's assume you just created a CAShapeLayer
(and by default, strokeEnd
is 1.0
). Then if I want to animate strokeEnd
from 0 to 100%, you can just use:
CABasicAnimation *stroke = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
stroke.fromValue = @(0.0);
stroke.duration = 1.0f;
[myLayer addAnimation:stroke forKey:nil];
Note, I did not have to specify the toValue
of @(1.0)
, because it will automatically default to the current value of myLayer.strokeEnd
(which happens to be 1.0 in this example).
Where these two disparate aspects of Core Animation interact is when you're writing an animation using CABasicAnimation
in which you not only want to specify the animation, but you also want to alter the properties of the layer, directly, right before the animation commences.
For example, if I added a CAShapeLayer
as a sublayer
, but wanted to animate the drawing of the first half of it (i.e. animate strokeEnd
from 0.0
to 0.5
), you might theoretically want to (a) specify myLayer.shapeEnd
to be the final value (e.g. 0.5
); and (b) create a CABasicAnimation
with a fromValue
of @(0.0)
(but you could omit toValue
because we've already set the layer's shapeEnd
to the appropriate value). But, going back to point 1 at the start of my answer, when we set a layer property directly, such as myLayer.shapeEnd
, you might want to setDisableActions
to YES
, so there's no attempt at any implicit animation (given that we're about to specify the actual desired animation with the CABasicAnimation
).
// set the layer's strokeEnd directly (with no implicit animation)
[CATransaction setDisableActions:YES];
myLayer.strokeEnd = 0.5;
[CATransaction setDisableActions:NO];
// now animate the layer's stroke end from 0.0 to the final value,
// which, because we didn't specify `toValue`, will default to the
// current value of `myLayer.strokeEnd` (which we happened to set
// right above)
CABasicAnimation *stroke = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
stroke.fromValue = @0.0;
stroke.duration = 1.0f;
[myLayer addAnimation:stroke forKey:nil];
In practice, I find the setDisableActions
is often unnecessary, but technically, if you're setting the layer's properties and you want to ensure that there's no implicit animation initiated, you may wish to use setDisableActions
, as shown above.
But, as you might infer from this discussion, the fact that we do not need to set toValue
is not a direct result of using CATransaction
or its method setDisableActions
. The toValue
is not needed in this example because we set the layer's strokeEnd
before starting the animation. We just happen to use CATransaction setDisableActions
to ensure there's no implicit animation initiated when changing layer properties directly.