CoreAnimation - Непрозрачность, плавное увеличение и уменьшение анимации не работает

Я пытаюсь создать довольно простой CoreAnimation для использования в AVComposition. Моя цель состоит в том, чтобы создать CALayer, который через различные подслои увеличивает и уменьшает заголовок, а затем постепенно исчезает в изображениях. По сути, слайд-шоу. Это экспортируется в .mov с использованием AVAssetWriter.

С помощью WWDC 2011 AVEditDemo я смог получить заголовок и изображения. Проблема в том, что они все одновременно отображаются на экране!

Я создал каждый слой с непрозрачностью 0,0. Затем я добавил CABasicAnimation, чтобы уменьшить их от 0,0 до 1,0, используя следующий код:

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];

Проблема, похоже, в свойстве beginTime. «1.0» означает задержку, поэтому она начинается через 1 секунду после начала анимации. Однако он сразу появляется на экране. Затухающая анимация

Оборотная сторона этого кода, для постепенного исчезновения, просто изменяет fromValue на 1.0 и toValue на 0.0. Он имеет время начала 4.0 и отлично работает.

Я использую следующее для создания 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;

Изображение исчезает в анимации с интервалом beginTime 5 секунд. Как и в названии, их анимация затухания работает нормально.

Любая помощь будет принята с благодарностью!

Ваше здоровье!

ИЗМЕНИТЬ

Все ответы были полезны, но в конечном итоге я обнаружил, что к CALayer можно добавить только одну анимацию. Анимация исчезновения работала, так как была добавлена ​​последней.

Затем я попробовал CAAnimationGroup, но это не сработало, поскольку я изменял тот же путь значения ключа.

Итак, я понял, что CAKeyframeAnimation лучше всего подходит для этого. Только вот с этим у меня тоже проблемы! Теперь код постепенно исчезает, но не исчезает. Я пробовал различные fillMode, менял продолжительность и т. Д. Не могу заставить его работать !!

Вот мой код:

    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]; 

person gamblor87    schedule 03.01.2012    source источник
comment
Итак, вы пытаетесь расположить и настроить все слои заранее, или вы можете добавить их по мере перехода слайдов?   -  person nielsbot    schedule 03.01.2012
comment
Позиционирую и настраиваю заранее. Я понял это благодаря этому сообщению: stackoverflow.com/questions/2184803/   -  person gamblor87    schedule 05.01.2012
comment
Вы смогли экспортировать CALayer в видео? Я застрял на этом сейчас. Если у вас есть какие-то советы, ответьте на мой вопрос. Спасибо! stackoverflow.com/questions/10285175/   -  person spring    schedule 24.04.2012
comment
Согласно документации Apple, время ключей должно иметь значение от 0,0 до 1,0: developer.apple .com / documentation / quartzcore /.   -  person Jakehao    schedule 10.10.2019


Ответы (10)


Я столкнулся с той же проблемой, и проблема в том, что один слой не может содержать постепенное появление и исчезновение. Таким образом, вы можете добавить другую анимацию к родительскому слою, как это сделал я.

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];
person Tim Kozak    schedule 04.07.2012
comment
Я бы никогда не догадался об этом. Спасибо @comonitos - person David John Smith; 25.07.2013
comment
@DavidJohnSmith рад помочь! - person Tim Kozak; 26.07.2013
comment
Возможно, это связано с обновлением iOS 7 SDK 2013-14 или чем-то, что я сделал иначе, но в настоящее время возможно добавление нескольких анимаций к одному слою, что делает ненужным использование дополнительного слоя @ комониты - person petershine; 10.02.2014

Блин, столько сложных ответов. Самый простой способ - просто добавить автореверс. Вуаля.

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"];
person Mikhail Larionov    schedule 17.04.2014
comment
Это решение запускает постепенное исчезновение, как только оно завершится. Я предполагаю, что OP хочет, чтобы заголовок исчез, немного оставался видимым, а затем исчез. - person oskare; 25.02.2015
comment
Действительно лучший ответ. Я хотел бы добавить для тех, кто борется, вы также можете добавить [yourView setWantsLayer: YES]; перед получением слоя вашего представления. По умолчанию в представлениях нет слоев. - person Borzh; 28.04.2015

Это работает для меня:

 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
person LenK    schedule 07.08.2015
comment
Хороший ответ :) in func - person YannSteph; 05.10.2015

Заимствуя ссылку, упомянутую gamblor87, и добавляя мои комментарии в качестве объяснения.

//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];
person SayeedHussain    schedule 15.03.2014
comment
fadeInAndOut.beginTime = 1.0 не работает, вам нужно использовать CACurrentMediaTime () + 1; - person Jay; 23.10.2014

Дело в ключевом имени. Вы должны установить ключ непрозрачности.

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

Проверьте образец кода. Я тестировал в 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")
person Den    schedule 27.10.2017
comment
Добавление: вы можете установить ключ либо в layer.add, либо в CAKeyframeAnimation(keyPath: "opacity") - person João Souza; 03.11.2019

Пытаться:

fadeInAnimation.beginTime = CACurrentMediaTime()+1.0;
person Peter DeWeese    schedule 03.01.2012

Суть проблемы заключалась в непонимании свойства keyTimes объекта CAKeyframeAnimation. Этот вопрос прояснил все и направил меня на правильный путь:

Какое значение является keyTime в CAKeyFrameAnimation?

person gamblor87    schedule 14.01.2012

Ответ LenK отлично сработал для меня. Если кому-то интересно, я переписал для Obj-C (обратите внимание, я также немного изменил ключевые кадры при постепенном появлении):

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;
person Ryan    schedule 23.01.2016
comment
У меня это сработало, но вам нужно добавить одну дополнительную строку: fadeInAndOutAnimation.repeatCount = HUGE_VALF;. В противном случае изображение исчезнет один раз и больше никогда не появится. - person rmaddy; 22.05.2016

Альтернативный способ - использование CAAnimationGroup. CAKeyframeAnimation также отлично работает для пользовательского интерфейса. Этот код отлично работает в пользовательском интерфейсе для отображения анимации в реальном времени. Но не будет работать вообще в AVVideoCompositionCoreAnimationTool - прокрутите вниз, если вам это нужно. Это не готовый код для копирования и вставки, но вы можете понять суть. Также вы можете добавить в группу дополнительные анимации:

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];
}

Небольшая заметка об анимации на слоях для AVVideoCompositionCoreAnimationTool. Вы можете увидеть соответствующее изображение в формате gif (заголовки должны появляться и исчезать один за другим). Чтобы решить эту проблему, я использую 2 отдельных CALayer, потому что по какой-то причине на одном слое 2 opaque анимации на нескольких слоях дают сбой.

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];
    }
}

И в конце мы можем получить правильную последовательность анимации  анимация справа

person WINSergey    schedule 30.06.2017

Посмотрите мой ответ https://stackoverflow.com/a/44204846/667483

В итоге: чтобы использовать beginTime, вы должны установить fillMode на kCAFillModeBackwards на вашем объекте анимации.

person surfrider    schedule 26.05.2017