I\'m attempting to create a fairly simple CoreAnimation for use in an AVComposition
. My goal is to create a CALayer
which, through various sublayer
The core of the problem was not understanding the keyTimes property of a CAKeyframeAnimation. This question clarified it all and set me down the right path:
What kind of value is keyTime in an CAKeyFrameAnimation?
This works for me:
let fadeAnimation = CAKeyframeAnimation(keyPath:"opacity")
fadeAnimation.beginTime = AVCoreAnimationBeginTimeAtZero + start
fadeAnimation.duration = duration
fadeAnimation.keyTimes = [0, 1/8.0, 5/8.0, 1]
fadeAnimation.values = [0.0, 1.0, 1.0, 0.0]
fadeAnimation.removedOnCompletion = false
fadeAnimation.fillMode = kCAFillModeForwards
layer.addAnimation(fadeAnimation, forKey:"animateOpacity")
layer.opacity = 0.0
I think you're looking for timeOffset
, not beginTime
...
Borrowing from the link mentioned by gamblor87 and adding my comments as explanation.
//create a fadeInOut CAKeyframeAnimation on opacticy
CAKeyframeAnimation *fadeInAndOut = [CAKeyframeAnimation animationWithKeyPath:@"opacity"];
//set duration
fadeInAndOut.duration = 5.0;
//autoreverses defaults to NO so we don't need this.
//fadeInAndOut.autoreverses = NO;
//keyTimes are time points on duration timeline as a fraction of animation duration (here 5 seconds).
fadeInAndOut.keyTimes = [NSArray arrayWithObjects:[NSNumber numberWithFloat:0.0],
[NSNumber numberWithFloat:0.20],
[NSNumber numberWithFloat:0.80],
[NSNumber numberWithFloat:1.0], nil];
//set opacity values at various points during the 5second animation
fadeInAndOut.values = [NSArray arrayWithObjects:[NSNumber numberWithFloat:0.0],//opacity 0 at 0s (corresponds to keyTime = 0s/5s)
[NSNumber numberWithFloat:1.0],//opacity 1 at 1s (corresponds to keyTime = 1s/5s)
[NSNumber numberWithFloat:1.0],//opacity 1 upto 4s (corresponds to keyTime = 4s/5s)
[NSNumber numberWithFloat:0.0],//opacity 0 at 5s (corresponds to keyTime = 5s/5s)
nil];
//delay in start of animation. What we are essentially saying is to start the 5second animation after 1second.
fadeInAndOut.beginTime = 1.0;
//don't remove the animation on completion.
fadeInAndOut.removedOnCompletion = NO;
//fill mode. In most cases we won't need this.
fadeInAndOut.fillMode = kCAFillModeBoth;
//add the animation to layer
[titleLayer addAnimation:fadeInAndOut forKey:nil];
The alternative way is the using CAAnimationGroup. CAKeyframeAnimation also works fine for UI. This code works fine in UI for show animation real-time. But will not work at all in AVVideoCompositionCoreAnimationTool
- please scroll down if you need it. It is not code ready for copy-paste but you can get the idea. Also, you can add addition animations to the group:
for (HKAnimatedLayer *animLayer in layersList) {
overlayLayer = [CALayer layer];
[overlayLayer setContents:(id)[animLayer.image CGImage]];
overlayLayer.frame = CGRectMake(0, 0, size.width, size.height);
[overlayLayer setMasksToBounds:YES];
NSMutableArray *animations = [NSMutableArray array];
// Step 1
{
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"opacity"];
animation.toValue = @(0);
animation.duration = kLayerFadeDuration;
animation.beginTime = kMovieDuration/5;
animation.fillMode = kCAFillModeForwards;
[animations addObject:animation];
}
{
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"opacity"];
animation.toValue = @(1.0);
animation.duration = kLayerFadeDuration;
animation.beginTime = kMovieDuration*2/3;
animation.fillMode = kCAFillModeForwards;
[animations addObject:animation];
}
CAAnimationGroup *animationGroup = [CAAnimationGroup animation];
animationGroup.animations = animations;
animationGroup.duration = kMovieDuration;
animationGroup.fillMode = kCAFillModeForwards;
animationGroup.removedOnCompletion = YES;
[overlayLayer addAnimation:animationGroup forKey:nil];
[parentLayer addSublayer:overlayLayer];
}
Here small note about animation on layers for AVVideoCompositionCoreAnimationTool
. You can see relevant gif image (titles should appear and disappear one by one). To solve this issue I use 2 separate CALayer
's because for some reason on one layer 2 opaque
animations on multiple layers are glitched.
// set up the parent layer
CALayer *parentLayer = [CALayer layer];
parentLayer.frame = CGRectMake(0, 0, size.width, size.height);
// one layer for one animation
CALayer *overlayLayer, *barrierLayer;
CABasicAnimation *animation;
for (HKAnimatedLayer *animLayer in layersList) {
overlayLayer = [CALayer layer];
overlayLayer.contents = (id)[animLayer.image CGImage];
overlayLayer.frame = CGRectMake(0, 0, size.width, size.height);
overlayLayer.masksToBounds = YES;
// layer with appear animation
if (animLayer.fromTime != 0 && (animLayer.fromTime - kLayerFadeDuration)>0) {
overlayLayer.opacity = 0.0;
animation = [CABasicAnimation animationWithKeyPath:@"opacity"];
animation.fromValue = @(0);
animation.toValue = @(1);
animation.additive = NO;
animation.removedOnCompletion = NO;
animation.beginTime = animLayer.fromTime - kLayerFadeDuration;
animation.duration = kLayerFadeDuration;
animation.fillMode = kCAFillModeForwards;
[overlayLayer addAnimation:animation forKey:@"fadeIn"];
}
if (animLayer.toTime == kMovieDuration) {
[parentLayer addSublayer:overlayLayer];
} else { // layer with dissappear animation
barrierLayer = [CALayer layer];
barrierLayer.frame = CGRectMake(0, 0, size.width, size.height);
barrierLayer.masksToBounds = YES;
[barrierLayer addSublayer:overlayLayer];
animation = [CABasicAnimation animationWithKeyPath:@"opacity"];
animation.fromValue = @(1);
animation.toValue = @(0);
animation.additive = NO;
animation.removedOnCompletion = NO;
animation.beginTime = animLayer.toTime;
animation.duration = kLayerFadeDuration;
animation.fillMode = kCAFillModeForwards;
[overlayLayer addAnimation:animation forKey:@"fadeOut"];
[parentLayer addSublayer:barrierLayer];
}
}
And at the end, we can get proper animation sequence
LenK's answer worked perfectly for me. If anyone is interested, I re-wrote for Obj-C (note I also slightly changed the key frames on fade in):
CAKeyframeAnimation *fadeInAndOutAnimation = [CAKeyframeAnimation animationWithKeyPath:@"opacity"];
fadeInAndOutAnimation.beginTime = CACurrentMediaTime() + beginTime;
fadeInAndOutAnimation.duration = duration;
fadeInAndOutAnimation.keyTimes = @[@0.0, @( 2.0 / 8.0 ), @( 5.0 / 8.0 ), @1.0];
fadeInAndOutAnimation.values = @[@0.0, @1.0, @1.0, @0.0];
fadeInAndOutAnimation.removedOnCompletion = false;
fadeInAndOutAnimation.fillMode = kCAFillModeForwards;