Подтвердить что ты не робот

CABasicAnimation сбрасывается до начального значения после завершения анимации

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

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

(xcode docs явно говорят, что анимация не обновит значение свойства.)

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

4b9b3361

Ответ 1

Вот ответ, это сочетание моего ответа и ответа Кришнана.

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

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

Ответ 2

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

Техника

I заключается в том, чтобы установить значение 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"];

Ответ 3

Задайте следующее свойство:

animationObject.removedOnCompletion = NO;

Ответ 4

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

CAAnimationGroup *theGroup = [CAAnimationGroup animation];

theGroup.fillMode = kCAFillModeForwards;

theGroup.removedOnCompletion = NO;

Ответ 5

Вы можете просто установить ключ 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

У вас может быть больше информации о 2011 Apple Video Video Session 421 - Основы основной анимации (середина видео)

Ответ 6

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

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

self.view.layer.opacity = 1;

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

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

Вытягивание значения из слоя презентации также особенно полезно для масштабирования или поворота ключей. (например, transform.scale, transform.rotation)

Ответ 7

Просто настройка 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-й размер не изменяется).

Ответ 8

Без использования 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 position when the animation finishes

    item.add(pathAnimation, forKey: nil)
}

Ответ 9

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

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

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

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

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

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

Ответ 10

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

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")

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

Ответ 11

Поэтому моя проблема заключалась в том, что я пытался вращать объект при жесте панорамирования, поэтому у меня было несколько одинаковых анимаций при каждом движении. У меня были оба fillMode = kCAFillModeForwards и isRemovedOnCompletion = false но это не помогло. В моем случае я должен был убедиться, что ключ анимации отличается каждый раз, когда я добавляю новую анимацию:

let angle = // here is my computed angle
let rotate = CABasicAnimation(keyPath: "transform.rotation.z")
rotate.toValue = angle
rotate.duration = 0.1
rotate.isRemovedOnCompletion = false
rotate.fillMode = kCAFillModeForwards

head.layer.add(rotate, forKey: "rotate\(angle)")

Ответ 12

Базовая анимация поддерживает две иерархии слоев: уровень модели и уровень представления. Когда анимация выполняется, слой модели фактически не поврежден и сохраняет свое первоначальное значение. По умолчанию анимация удаляется после ее завершения. Затем уровень представления возвращается к значению уровня модели.

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

Так что было бы лучшим решением обновить свойство непосредственно на уровне модели до конечного значения.

self.view.layer.opacity = 1;
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"opacity"];
animation.fromValue = 0;
animation.toValue = 1;
[self.view.layer addAnimation:animation forKey:nil];

Если есть неявная анимация, вызванная первой строкой кода выше, попробуйте отключить, если:

[CATransaction begin];
[CATransaction setDisableActions:YES];
self.view.layer.opacity = 1;
[CATransaction commit];

CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"opacity"];
animation.fromValue = 0;
animation.toValue = 1;
[self.view.layer addAnimation:animation forKey:nil];

Ответ 13

Кажется, что removeOnCompletion, установленный в false, и fillMode, установленный в kCAFillModeForwards, тоже не работает для меня.

После того, как я применил новую анимацию на слое, анимационный объект сбрасывается в исходное состояние, а затем анимируется из этого состояния. Что еще нужно сделать, так это установить желаемое свойство слоя модели в соответствии с его уровнем уровня представления перед установкой новой анимации следующим образом:

someLayer.path = ((CAShapeLayer *)[someLayer presentationLayer]).path;
[someLayer addAnimation:someAnimation forKey:@"someAnimation"];

Ответ 14

Самое простое решение - использовать неявную анимацию. Это решит все эти проблемы для вас:

self.layer?.backgroundColor = NSColor.red.cgColor;

Если вы хотите настроить, например, продолжительность, вы можете использовать NSAnimationContext:

    NSAnimationContext.beginGrouping();
    NSAnimationContext.current.duration = 0.5;
    self.layer?.backgroundColor = NSColor.red.cgColor;
    NSAnimationContext.endGrouping();

Примечание: это проверено только на macOS.

Я изначально не видел никакой анимации при этом. Проблема заключается в том, что уровень слоя с поддержкой представления не подразумевает анимацию. Чтобы решить эту проблему, убедитесь, что вы добавляете слой самостоятельно (перед установкой представления на слой-основу).

Пример, как это сделать:

override func awakeFromNib() {
    self.layer = CALayer();
    //self.wantsLayer = true;
}

Использование self.wantsLayer не имело никакого значения в моем тестировании, но оно может иметь некоторые побочные эффекты, о которых я не знаю.

Ответ 15

Вот образец с детской площадки:

import PlaygroundSupport
import UIKit

let resultRotation = CGFloat.pi / 2
let view = UIView(frame: CGRect(x: 0.0, y: 0.0, width: 200.0, height: 300.0))
view.backgroundColor = .red

//--------------------------------------------------------------------------------

let rotate = CABasicAnimation(keyPath: "transform.rotation.z") // 1
rotate.fromValue = CGFloat.pi / 3 // 2
rotate.toValue = resultRotation // 3
rotate.duration = 5.0 // 4
rotate.beginTime = CACurrentMediaTime() + 1.0 // 5
// rotate.isRemovedOnCompletion = false // 6
rotate.fillMode = .backwards // 7

view.layer.add(rotate, forKey: nil) // 8
view.layer.setAffineTransform(CGAffineTransform(rotationAngle: resultRotation)) // 9

//--------------------------------------------------------------------------------

PlaygroundPage.current.liveView = view
  1. Создать анимационную модель
  2. Установить начальную позицию анимации (может быть пропущено, это зависит от вашего текущего макета слоя)
  3. Установить конечную позицию анимации
  4. Установить продолжительность анимации
  5. Задержка анимации на секунду
  6. Не устанавливайте false на isRemovedOnCompletion - пусть Core Animation очищается после завершения анимации
  7. Вот первая часть трюка - вы говорите Core Animation, чтобы поместить анимацию в начальную позицию (вы установили на шаге 2) еще до того, как анимация была запущена - продлить ее назад во времени
  8. Скопируйте подготовленный объект анимации, добавьте его в слой и запустите анимацию после задержки (вы установили на шаге 5)
  9. Вторая часть - установить правильную конечную позицию слоя - после удаления анимации ваш слой будет показан в правильном месте