Graphics Reference
In-Depth Information
Taking that into account, if we insert the following two lines between where we create our
animation and where we add it to the layer, it should get rid of the snap-back:
animation.fromValue = (
__bridge
id
)
self
.
colorLayer
.backgroundColor;
self
.
colorLayer
.backgroundColor = color.CGColor;
That works, but is potentially unreliable. We should really derive the
fromValue
from the
presentation
layer (if it exists) rather than the model layer, in case there is already an
animation in progress. Also, because the layer in this case is
not
a backing layer, we should
disable implicit animations using a
CATransaction
before setting the property, or the
default layer action may interfere with our explicit animation. (In practice, the explicit
animation always seems to override the implicit one, but this behavior is not documented,
so it's better to be safe than sorry.)
If we make those changes, we end up with the following:
CALayer
*layer =
self
.
colorLayer
.
presentationLayer
?:
self
.
colorLayer;
animation.
fromValue
= (
__bridge
id
)layer.
backgroundColor
;
[
CATransaction
begin
];
[
CATransaction
setDisableActions
:
YES
];
self
.
colorLayer
.
backgroundColor
= color.
CGColor
;
[
CATransaction
commit
];
That's quite a lot of code to have to add to each and every animation. Fortunately, we can
derive this information automatically from the
CABasicAnimation
object itself, so we can
create a reusable method. Listing 8.2 shows a modified version of our first example that
includes a method for applying a
CABasicAnimation
without needing to repeat this
boilerplate code each time.
Listing 8.2
A Reusable Method for Fixing Animation Snap-Back
- (
void
)applyBasicAnimation:(
CABasicAnimation
*)animation
toLayer:(
CALayer
*)layer
{
//set the from value (using presentation layer if available)
animation.
fromValue
= [layer.
presentationLayer ?: layer
valueForKeyPath
:animation.
keyPath
];
//update the property in advance
//note: this approach will only work if toValue != nil
[
CATransaction
begin
];
[
CATransaction
setDisableActions
:
YES
];
[layer
setValue
:animation.
toValue
forKeyPath
:animation.
keyPath
];
[
CATransaction
commit
];
//apply animation to layer
[layer
addAnimation
:animation
forKey
:
nil
];
}