CoreAnimation - Opacity Fade In and Out Animation

2019-01-16 20:03发布

I'm attempting to create a fairly simple CoreAnimation for use in an AVComposition. My goal is to create a CALayer which, through various sublayers, fades a title in and out, then fades in an out images. A slideshow, basically. This is being exported to a .mov using AVAssetWriter.

With help from the WWDC 2011 AVEditDemo, I've been able to get a title and images appearing. The problem is that they are all on screen at the same time!

I have created each layer with an opacity of 0.0. I have then added an CABasicAnimation to fade them from 0.0 to 1.0, using the following code:

CABasicAnimation *fadeInAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"];
fadeInAnimation.fromValue = [NSNumber numberWithFloat:0.0];
fadeInAnimation.toValue = [NSNumber numberWithFloat:1.0];
fadeInAnimation.additive = NO;
fadeInAnimation.removedOnCompletion = YES;
fadeInAnimation.beginTime = 1.0;
fadeInAnimation.duration = 1.0;
fadeInAnimation.fillMode = kCAFillModeForwards;
[titleLayer addAnimation:fadeInAnimation forKey:nil];

The problem seems to be the 'beginTime' property. The "1.0" is meant to be a delay, so it starts 1 second after the start of the animation. However, it is appearing on the screen straight away. A fade out animation

The reverse of this code, for the fade out, simply changes the fromValue to 1.0 and the toValue to 0.0. It has a begin time of 4.0 and works perfectly.

I'm using the following to create the animatedTitleLayer:

CATextLayer *titleLayer = [CATextLayer layer];
titleLayer.string =self.album.title;
titleLayer.font = @"Helvetica";
titleLayer.fontSize = videoSize.height / 6;
titleLayer.alignmentMode = kCAAlignmentCenter;
titleLayer.bounds = CGRectMake(0, 0, videoSize.width, videoSize.height / 6);
titleLayer.foregroundColor = [[UIColor redColor]CGColor];
titleLayer.opacity = 0.0;

The image fade in animations have a beginTime 5 seconds apart. Like the title, their fade out animations work fine.

Any help would be greatly appreciated!

Cheers!

EDIT

The answers were all helpful, but ultimately I discovered that only one animation could be added to a CALayer. The fade out animation was working as it was the last one added.

I then tried a CAAnimationGroup, but this didn't work as I was modifying the same key value path.

So I've realised that a CAKeyframeAnimation is the best for this. Only I'm having some difficulty with that too! The code is now fading in okay, but it isn't fading out. I've tried various fillMode's, changed the duration, etc. Can't make it work!!

Here is my code:

    CAKeyframeAnimation *fadeInAndOut = [CAKeyframeAnimation animationWithKeyPath:@"opacity"];
    fadeInAndOut.duration = 5.0;
    fadeInAndOut.autoreverses = NO;
    fadeInAndOut.keyTimes = [NSArray arrayWithObjects:  [NSNumber numberWithFloat:0.0],
                                                            [NSNumber numberWithFloat:1.0],
                                                    [NSNumber numberWithFloat:4.0], 
                                                    [NSNumber numberWithFloat:5.0], nil];

    fadeInAndOut.values = [NSArray arrayWithObjects:    [NSNumber numberWithFloat:0.0],
                                                    [NSNumber numberWithFloat:1.0],
                                                    [NSNumber numberWithFloat:1.0], 
                                                    [NSNumber numberWithFloat:0.0], nil];
    fadeInAndOut.beginTime = 1.0;
    fadeInAndOut.removedOnCompletion = NO;
    fadeInAndOut.fillMode = kCAFillModeBoth;
    [titleLayer addAnimation:fadeInAndOut forKey:nil]; 

11条回答
我想做一个坏孩纸
2楼-- · 2019-01-16 20:15

Man, so many complicated answers. The simplest way is just to add autoreverse. Voila.

CABasicAnimation *fadeInAndOut = [CABasicAnimation animationWithKeyPath:@"opacity"];
fadeInAndOut.duration = 5.0;
fadeInAndOut.autoreverses = YES;
fadeInAndOut.fromValue = [NSNumber numberWithFloat:0.0];
fadeInAndOut.toValue = [NSNumber numberWithFloat:1.0];
fadeInAndOut.repeatCount = HUGE_VALF;
fadeInAndOut.fillMode = kCAFillModeBoth;
[titleLayer addAnimation:fadeInAndOut forKey:@"myanimation"];
查看更多
We Are One
3楼-- · 2019-01-16 20:17

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.

AVVideoCompositionCoreAnimationTool

// 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 right animation

查看更多
看我几分像从前
4楼-- · 2019-01-16 20:18

I'm faced the same issue and the problem is - one layer can't contain fade in and out. So you can add the other animation to the parent layer as i did

CALayer *parentLayer = [CALayer layer];
CALayer *animtingLayer = [CALayer layer];

//FADE IN
        CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"opacity"];
        animation.beginTime = CMTimeGetSeconds(img.startTime);
        animation.duration = CMTimeGetSeconds(_timeline.transitionDuration);
        animation.fromValue = [NSNumber numberWithFloat:0.0f];
        animation.toValue = [NSNumber numberWithFloat:1.0f];
        animation.removedOnCompletion = NO;
        animation.fillMode = kCAFillModeBoth;
        animation.additive = NO;
        [parentLayer addAnimation:animation forKey:@"opacityIN"];

//FADE OUT
        CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"opacity"];
        animation.beginTime = CMTimeGetSeconds(CMTimeAdd(img.passTimeRange.start, img.passTimeRange.duration));
        animation.duration = CMTimeGetSeconds(_timeline.transitionDuration);
        animation.fromValue = [NSNumber numberWithFloat:1.0f];
        animation.toValue = [NSNumber numberWithFloat:0.0f];
        animation.removedOnCompletion = NO;
        animation.fillMode = kCAFillModeBoth;
        animation.additive = NO;
        [animtingLayer addAnimation:animation forKey:@"opacityOUT"];

    [parentLayer addSublayer:animtingLayer];
查看更多
叛逆
5楼-- · 2019-01-16 20:19

The point is key name. You have to set the opacity key.

layer.add(animation, forKey: nil)         // Not Working
layer.add(animation, forKey: "opacity")   // Working

Check the sample code. I tested in Swift 4

    let animation                   = CAKeyframeAnimation()
    animation.duration              = 1.53
    animation.autoreverses          = false
    animation.keyTimes              = [0, 0.51, 0.85, 1.0]
    animation.values                = [0.5, 0.5, 1.0, 0.5]
    animation.beginTime             = 0
    animation.isRemovedOnCompletion = false
    animation.fillMode              = kCAFillModeBoth
    animation.repeatCount           = .greatestFiniteMagnitude
    layer.add(animation, forKey: "opacity")
查看更多
时光不老,我们不散
6楼-- · 2019-01-16 20:22

Try:

fadeInAnimation.beginTime = CACurrentMediaTime()+1.0;
查看更多
老娘就宠你
7楼-- · 2019-01-16 20:23

I think you're looking for timeOffset, not beginTime...

查看更多
登录 后发表回答