I've made a wrapper for compact creation of CABasicAnimation instances.
It's implemented through a category for UIView as an instance method named change:from:to:in:ease:delay:done:
. So for example, I can do:
[self.logo
change:@"y"
from:nil // Use current self.logo.layer.position.y
to:@80
in:1 // Finish in 1000 ms
ease:@"easeOutQuad" // A selector for a CAMediaTimingFunction category method
delay:0
done:nil];
The problem
When the CABasicAnimation starts, animationDidStart:
handles setting self.logo.layer.position.y
to 80 (the end value). Before it worked like this, I tried using animationDidStop:finished:
to do the same thing, but found the layer flickering after completing the animation. Now, the layer goes straight to the end value, and no interpolation occurs. I implemented animationDidStart:
in my UIView category like so:
- (void)animationDidStart:(CAAnimation *)animation
{
[self.layer
setValue:[animation valueForKey:@"toValue"]
forKeyPath:[animation valueForKey:@"keyPath"]];
}
I'm setting the end value in order to match the model layer with the presentation layer (in other words, to prevent resetting back to the start position).
Here's the implementation to change:from:to:in:ease:delay:done:
...
- (CABasicAnimation*) change:(NSString*)propertyPath
from:(id)from
to:(id)to
in:(CGFloat)seconds
ease:(NSString*)easeName
delay:(CGFloat)delay
done:(OnDoneCallback)done
{
NSString* keyPath = [app.CALayerAnimationKeyPaths objectForKey:propertyPath];
if (keyPath == nil) keyPath = propertyPath;
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:keyPath];
if (delay > 0) animation.beginTime = CACurrentMediaTime() + delay;
if (easeName != nil) animation.timingFunction = [CAMediaTimingFunction performSelector:NSSelectorFromString(easeName)];
if (from != nil) animation.fromValue = from;
animation.toValue = to;
animation.duration = seconds;
animation.delegate = self;
[self.layer setValue:done forKey:@"onDone"];
[self.layer addAnimation:animation forKey:keyPath];
return animation;
}
In the first line of the above code, this is the NSDictionary I use to convert property shortcuts to the real keyPath. This is so I can just type @"y"
instead of @"position.y"
every time.
app.CALayerAnimationKeyPaths = @{
@"scale": @"transform.scale",
@"y": @"position.y",
@"x": @"position.x",
@"width": @"frame.size.width",
@"height": @"frame.size.height",
@"alpha": @"opacity",
@"rotate": @"transform.rotation"
};
Any questions?