CABasicAnimation resets to initial value after ani

2019-01-16 00:50发布

I am rotating a CALayer and trying to stop it at its final position after animation is completed.

But after animation completes it resets to its initial position.

(xcode docs explicitly say that the animation will not update the value of the property.)

any suggestions how to achieve this.

14条回答
迷人小祖宗
2楼-- · 2019-01-16 01:15

It seems that removedOnCompletion flag set to false and fillMode set to kCAFillModeForwards doesn't work for me either.

After I apply new animation on a layer, an animating object resets to its initial state and then animates from that state. What has to be done additionally is to set the model layer's desired property according to its presentation layer's property before setting new animation like so:

someLayer.path = ((CAShapeLayer *)[someLayer presentationLayer]).path;
[someLayer addAnimation:someAnimation forKey:@"someAnimation"];
查看更多
男人必须洒脱
3楼-- · 2019-01-16 01:15

The easiest solution is to use implicit animations. This will handle all of that trouble for you:

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

If you want to customize e.g. the duration, you can use NSAnimationContext:

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

Note: This is only tested on macOS.

I initially did not see any animation when doing this. The problem is that the layer of a view-backed layer does not implicit animate. To solve this, make sure you add a layer yourself (before setting the view to layer-backed).

An example how to do this would be:

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

Using self.wantsLayer did not make any difference in my testing, but it could have some side effects that I do not know of.

查看更多
放荡不羁爱自由
4楼-- · 2019-01-16 01:16

Simply setting fillMode and removedOnCompletion didn't work for me. I solved the problem by setting all of the properties below to the CABasicAnimation object:

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

This code transforms myView to 85% of its size (3rd dimension unaltered).

查看更多
Melony?
5楼-- · 2019-01-16 01:21

A CALayer has a model layer and a presentation layer. During an animation, the presentation layer updates independently of the model. When the animation is complete, the presentation layer is updated with the value from the model. If you want to avoid a jarring jump after the animation ends, the key is to keep the two layers in sync.

If you know the end value, you can just set the model directly.

self.view.layer.opacity = 1;

But if you have an animation where you don't know the end position (e.g. a slow fade that the user can pause and then reverse), then you can query the presentation layer directly to find the current value, and then update the model.

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

Pulling the value from the presentation layer is also particularly useful for scaling or rotation keypaths. (e.g. transform.scale, transform.rotation)

查看更多
啃猪蹄的小仙女
6楼-- · 2019-01-16 01:22

@Leslie Godwin's answer is not really good, "self.view.layer.opacity = 1;" is done immediately (it takes about one second), please fix alphaAnimation.duration to 10.0, if you have doubts. You have to remove this line.

So, when you fix fillMode to kCAFillModeForwards and removedOnCompletion to NO, you let the animation remains in the layer. If you fix the animation delegate and try something like:

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

...the layer restores immediately at the moment you execute this line. It's what we wanted to avoid.

You must fix the layer property before remove the animation from it. Try this:

- (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];
        }
    }
}
查看更多
beautiful°
7楼-- · 2019-01-16 01:22

Core animation maintains two layer hierarchies: the model layer and the presentation layer. When the animation is in progress, the model layer is actually intact and keeps it initial value. By default, the animation is removed once the it's completed. Then the presentation layer falls back to the value of the model layer.

Simply setting removedOnCompletion to NO means the animation won't be removed and wastes memory. In addition, the model layer and the presentation layer won't be synchronous any more, which may lead to potential bugs.

So it would be a better solution to update the property directly on the model layer to the final value.

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

If there's any implicit animation caused by the first line of above code, try to turn if off:

[CATransaction begin];
[CATransaction setDisableActions:YES];

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

[CATransaction commit];
查看更多
登录 后发表回答