动画完成后,CABasicAnimation将重置为初始值

问题描述 投票:122回答:14

我正在旋转CALayer,并在动画完成后尝试将其停在最终位置。

但是在动画完成后,它会重置到其初始位置。

(xcode docs明确表示动画不会更新属性的值。)

任何建议如何实现这一目标。

ios iphone calayer cabasicanimation
14个回答
258
投票

编辑:这是答案,这是我的答案和Krishnan的结合。

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

默认值为kCAFillModeRemoved。 (这是你看到的重置行为。)


3
投票

这有效:

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直到最终处理该层 - 避免。


2
投票

所以我的问题是我试图在平移手势上旋转一个对象,所以我在每次移动时都有多个相同的动画。我有fillMode = kCAFillModeForwardsisRemovedOnCompletion = 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)")

2
投票

核心动画维护着两层层次结构:the model layerthe presentation layer。当动画正在进行时,模型层实际上是完整的并保持其初始值。默认情况下,动画一旦完成就会被删除。然后表示层回退到模型层的值。

简单地将removedOnCompletion设置为NO意味着不会删除动画并浪费内存。此外,模型层和表示层将不再同步,这可能导致潜在的错误。

因此,将模型层上的属性直接更新为最终值将是更好的解决方案。

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

1
投票

似乎removedOnCompletion标志设置为false而fillMode设置为kCAFillModeForwards对我来说也不起作用。

在图层上应用新动画后,动画对象将重置为其初始状态,然后从该状态进行动画处理。另外需要做的是在设置新动画之前根据其表示层的属性设置模型图层的所需属性,如下所示:

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

0
投票

最简单的解决方案是使用隐式动画。这将为您解决所有麻烦:

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对我的测试没有任何影响,但它可能会产生一些我不知道的副作用。


78
投票

removedOnCompletion的问题是UI元素不允许用户交互。

我的技术是在动画中设置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
投票

设置以下属性:

animationObject.removedOnCompletion = NO;

12
投票

把它放在你的代码中

CAAnimationGroup *theGroup = [CAAnimationGroup animation];

theGroup.fillMode = kCAFillModeForwards;

theGroup.removedOnCompletion = NO;

11
投票

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 WWDC Video Session 421 - Core Animation Essentials(视频中间)的更多信息


7
投票

简单地设置fillModeremovedOnCompletion并不适合我。我通过将下面的所有属性设置为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%(第三维未经改变)。


5
投票

CALayer具有模型层和表示层。在动画期间,表示层独立于模型进行更新。动画完成后,将使用模型中的值更新表示层。如果你想在动画结束后避免震动跳跃,关键是保持两层同步。

如果您知道最终值,则可以直接设置模型。

self.view.layer.opacity = 1;

但是如果你有一个你不知道结束位置的动画(例如用户可以暂停然后反转的缓慢淡入淡出),那么你可以直接查询表示层以找到当前值,然后更新模型。

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

从表示层中提取值对于缩放或旋转键路径也特别有用。 (例如transform.scaletransform.rotation


5
投票

不使用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)
}

4
投票

@Leslie Godwin的回答并不是很好,“self.view.layer.opacity = 1;”立即完成(大约需要一秒钟),如果您有疑问,请将alphaAnimation.duration修复为10.0。你必须删除这一行。

因此,当您将fillMode修复为kCAFillModeForwards并将removeOnCompletion修复为NO时,您将动画保留在图层中。如果您修复动画委托并尝试以下方法:

- (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];
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.