CABasicAnimation сбрасывается до начального значения после завершения анимации - PullRequest
127 голосов
/ 19 мая 2011

Я вращаю CALayer и пытаюсь остановить его в его окончательном положении после завершения анимации.

Но после завершения анимации она сбрасывается в исходное положение.

(документы xcode явно говорят, что анимация не будет обновлять значение свойства.)

любые предложения, как этого добиться.

Ответы [ 14 ]

257 голосов
/ 20 мая 2011

Редактировать : Вот ответ, это комбинация моего ответа и ответа Кришнана.

cabasicanimation.fillMode = kCAFillModeForwards;
cabasicanimation.removedOnCompletion = NO;

Значением по умолчанию является kCAFillModeRemoved. (Какое поведение сброса вы видите.)

77 голосов
/ 13 декабря 2012

Проблема с removeOnCompletion заключается в том, что элемент пользовательского интерфейса не позволяет взаимодействовать с пользователем.

Я использую метод FROM для установки значения FROM в анимации и значения TO для объекта.Анимация автоматически заполняет значение TO до ее запуска, а после удаления объект остается в правильном состоянии.

// fade in
CABasicAnimation *alphaAnimation = [CABasicAnimation animationWithKeyPath: @"opacity"];
alphaAnimation.fillMode = kCAFillModeForwards;

alphaAnimation.fromValue = NUM_FLOAT(0);
self.view.layer.opacity = 1;

[self.view.layer addAnimation: alphaAnimation forKey: @"fade"];
16 голосов
/ 19 мая 2011

Установите следующее свойство:

animationObject.removedOnCompletion = NO;
12 голосов
/ 24 февраля 2012

просто поместите его в свой код

CAAnimationGroup *theGroup = [CAAnimationGroup animation];

theGroup.fillMode = kCAFillModeForwards;

theGroup.removedOnCompletion = NO;
10 голосов
/ 05 сентября 2013

Вы можете просто установить ключ CABasicAnimation на position, когда добавляете его в слой. Делая это, он переопределит неявную анимацию, выполненную в позиции для текущего прохода в цикле выполнения.

CGFloat yOffset = 30;
CGPoint endPosition = CGPointMake(someLayer.position.x,someLayer.position.y + yOffset);

someLayer.position = endPosition; // Implicit animation for position

CABasicAnimation * animation =[CABasicAnimation animationWithKeyPath:@"position.y"]; 

animation.fromValue = @(someLayer.position.y);
animation.toValue = @(someLayer.position.y + yOffset);

[someLayer addAnimation:animation forKey:@"position"]; // The explicit animation 'animation' override implicit animation

Вы можете получить больше информации о Apple WWDC Video Session 421 - Основные принципы анимации (середина видео).

7 голосов
/ 12 февраля 2016

Просто установить fillMode и removedOnCompletion у меня не сработало.Я решил проблему, установив все свойств ниже для объекта CABasicAnimation:

CABasicAnimation* ba = [CABasicAnimation animationWithKeyPath:@"transform"];
ba.duration = 0.38f;
ba.fillMode = kCAFillModeForwards;
ba.removedOnCompletion = NO;
ba.autoreverses = NO;
ba.repeatCount = 0;
ba.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(0.85f, 0.85f, 1.0f)];
[myView.layer addAnimation:ba forKey:nil];

Этот код преобразует myView до 85% его размера (3-е измерение без изменений).

5 голосов
/ 30 июля 2017

Без использования removedOnCompletion

Вы можете попробовать эту технику:

self.animateOnX(item: shapeLayer)

func animateOnX(item:CAShapeLayer)
{
    let endPostion = CGPoint(x: 200, y: 0)
    let pathAnimation = CABasicAnimation(keyPath: "position")
    //
    pathAnimation.duration = 20
    pathAnimation.fromValue = CGPoint(x: 0, y: 0)//comment this line and notice the difference
    pathAnimation.toValue =  endPostion
    pathAnimation.fillMode = kCAFillModeBoth

    item.position = endPostion//prevent the CABasicAnimation from resetting item's position when the animation finishes

    item.add(pathAnimation, forKey: nil)
}
5 голосов
/ 22 декабря 2015

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

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

self.view.layer.opacity = 1;

Но если у вас есть анимация, в которой вы не знаете конечную позицию (например, медленное затухание, которое пользователь может приостановить, а затем развернуть), то вы можете напрямую запросить уровень представления, чтобы найти текущее значение, а затем обновить модель.

NSNumber *opacity = [self.layer.presentationLayer valueForKeyPath:@"opacity"];
[self.layer setValue:opacity forKeyPath:@"opacity"];

Извлечение значения из уровня представления также особенно полезно для масштабирования или вращения траекторий. (например, transform.scale, transform.rotation)

4 голосов
/ 19 сентября 2014

@ Ответ Лесли Годвина не очень хорош, "self.view.layer.opacity = 1;"выполняется немедленно (это занимает около одной секунды), пожалуйста, исправьте alphaAnimation.duration на 10.0, если у вас есть сомнения.Вы должны удалить эту строку.

Таким образом, когда вы фиксируете fillMode в kCAFillModeForwards и удалили OnCompletion в NO, вы разрешаете анимации оставаться в слое.Если вы исправите делегат анимации и попробуете что-то вроде:

- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
{
 [theLayer removeAllAnimations];
}

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

Вы должны исправить свойство layer перед удалением из него анимации.Попробуйте это:

- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
{
     if([anim isKindOfClass:[CABasicAnimation class] ]) // check, because of the cast
    {
        CALayer *theLayer = 0;
        if(anim==[_b1 animationForKey:@"opacity"])
            theLayer = _b1; // I have two layers
        else
        if(anim==[_b2 animationForKey:@"opacity"])
            theLayer = _b2;

        if(theLayer)
        {
            CGFloat toValue = [((CABasicAnimation*)anim).toValue floatValue];
            [theLayer setOpacity:toValue];

            [theLayer removeAllAnimations];
        }
    }
}
3 голосов
/ 10 февраля 2016

Это работает:

let animation = CABasicAnimation(keyPath: "opacity")
animation.fromValue = 0
animation.toValue = 1
animation.duration = 0.3

someLayer.opacity = 1 // important, this is the state you want visible after the animation finishes.
someLayer.addAnimation(animation, forKey: "myAnimation")

Базовая анимация показывает «слой представления» поверх вашего обычного слоя во время анимации.Так что установите непрозрачность (или что-то еще) на то, что вы хотите видеть, когда анимация заканчивается, и слой представления исчезает.Сделайте это в строке до , добавив анимацию, чтобы избежать мерцания при ее завершении.

Если вы хотите задержать, сделайте следующее:

let animation = CABasicAnimation(keyPath: "opacity")
animation.fromValue = 0
animation.toValue = 1
animation.duration = 0.3
animation.beginTime = someLayer.convertTime(CACurrentMediaTime(), fromLayer: nil) + 1
animation.fillMode = kCAFillModeBackwards // So the opacity is 0 while the animation waits to start.

someLayer.opacity = 1 // <- important, this is the state you want visible after the animation finishes.
someLayer.addAnimation(animation, forKey: "myAnimation")

Наконец, если вы используете'chedOnCompletion = false ', это приведет к утечке CAAnimations, пока слой не будет в конечном итоге удален - избегайте.

...