简体   繁体   English

动画期间无法获取 CALayer 的当前位置

[英]Cannot get current position of CALayer during animation

I am trying to achieve an animation that when you hold down a button it animates a block down, and when you release, it animates it back up to the original position, but I cannot obtain the current position of the animating block no matter what.我试图实现一个动画,当你按住一个按钮时,它会向下移动一个块,当你释放时,它会让它回到原始位置,但无论如何我都无法获得动画块的当前位置。 Here is my code:这是我的代码:

-(IBAction)moveDown:(id)sender{

   CGRect position = [[container.layer presentationLayer] frame];

    [movePath moveToPoint:CGPointMake(container.frame.origin.x, position.y)];

    [movePath addLineToPoint:CGPointMake(container.frame.origin.x, 310)];

    CAKeyframeAnimation *moveAnim = [CAKeyframeAnimation animationWithKeyPath:@"position"];
    moveAnim.path = movePath.CGPath;
    moveAnim.removedOnCompletion = NO;
    moveAnim.fillMode = kCAFillModeForwards;


    CAAnimationGroup *animGroup = [CAAnimationGroup animation];
    animGroup.animations = [NSArray arrayWithObjects:moveAnim, nil];
    animGroup.duration = 2.0;
    animGroup.removedOnCompletion = NO;
    animGroup.fillMode = kCAFillModeForwards;

    [container.layer addAnimation:animGroup forKey:nil];
}
-(IBAction)moveUp:(id)sender{
     CGRect position = [[container.layer presentationLayer] frame];

    UIBezierPath *movePath = [UIBezierPath bezierPath];

    [movePath moveToPoint:CGPointMake(container.frame.origin.x, position.y)];

    [movePath addLineToPoint:CGPointMake(container.frame.origin.x, 115)];

    CAKeyframeAnimation *moveAnim = [CAKeyframeAnimation animationWithKeyPath:@"position"];
    moveAnim.path = movePath.CGPath;
    moveAnim.removedOnCompletion = NO;
    moveAnim.fillMode = kCAFillModeForwards;

    CAAnimationGroup *animGroup = [CAAnimationGroup animation];
    animGroup.animations = [NSArray arrayWithObjects:moveAnim, nil];
    animGroup.duration = 2.0;
    animGroup.removedOnCompletion = NO;
    animGroup.fillMode = kCAFillModeForwards;

    [container.layer addAnimation:animGroup forKey:nil];

}

But the line但是线

   CGRect position = [[container.layer presentationLayer] frame];

is only returning the destination position not the current position.只返回目标位置而不是当前位置。 I need to basically give me the current position of the container thats animating once I release the button, so I can perform the next animation.一旦我释放按钮,我基本上需要给我动画容器的当前位置,以便我可以执行下一个动画。 What I have now does not work.我现在所拥有的不起作用。

I haven't analyzed your code enough to be 100% sure why [[container.layer presentationLayer] frame] might not return what you expect.我还没有对您的代码进行足够的分析,无法 100% 确定为什么[[container.layer presentationLayer] frame]可能不会返回您期望的内容。 But I see several problems.但我看到了几个问题。

One obvious problem is that moveDown: doesn't declare movePath .一个明显的问题是moveDown:没有声明movePath If movePath is an instance variable, you probably want to clear it or create a new instance each time moveDown: is called, but I don't see you doing that.如果movePath是一个实例变量,您可能希望在每次调用moveDown:时清除它或创建一个新实例,但我没有看到您这样做。

A less obvious problem is that (judging from your use of removedOnCompletion and fillMode , in spite of your use of presentationLayer ) you apparently don't understand how Core Animation works.一个不太明显的问题是(从您使用removedOnCompletionfillMode来看,尽管您使用了presentationLayer )您显然不了解 Core Animation 是如何工作的。 This turns out to be surprisingly common, so forgive me if I'm wrong.事实证明这出奇的普遍,所以如果我错了,请原谅我。 Anyway, read on, because I will explain how Core Animation works and then how to fix your problem.无论如何,请继续阅读,因为我将解释 Core Animation 的工作原理以及如何解决您的问题。

In Core Animation, the layer object you normally work with is a model layer .在 Core Animation 中,您通常使用的图层对象是模型图层 When you attach an animation to a layer, Core Animation creates a copy of the model layer, called the presentation layer , and the animation changes the properties of the presentation layer over time.当您将动画附加到层时,Core Animation 会创建模型层的副本,称为表示层,动画会随着时间的推移更改表示层的属性。 An animation never changes the properties of the model layer.动画永远不会改变模型层的属性。

When the animation ends, and (by default) is removed, the presentation layer is destroyed and the values of the model layer's properties take effect again.当动画结束并(默认情况下)被移除时,表示层将被销毁,模型层的属性值将再次生效。 So the layer on screen appears to “snap back” to its original position/color/whatever.因此,屏幕上的图层似乎“回弹”到其原始位置/颜色/任何内容。

A common, but wrong way to fix this is to set the animation's removedOnCompletion to NO and its fillMode to kCAFillModeForwards .一种常见但错误的解决方法是将动画的removedOnCompletion设置为NO并将其fillModekCAFillModeForwards When you do this, the presentation layer hangs around, so there's no “snap back” on screen.执行此操作时,表示层会挂起,因此屏幕上不会“快速返回”。 The problem is that now you have the presentation layer hanging around with different values than the model layer.问题是,现在您的表示层具有与模型层不同的值。 If you ask the model layer (or the view that owns it) for the value of the animated property, you'll get a value that's different than what's on screen.如果您向模型层(或拥有它的视图)询问动画属性的值,您将得到一个与屏幕上不同的值。 And if you try to animate the property again, the animation will probably start from the wrong place.如果您再次尝试为该属性设置动画,动画可能会从错误的位置开始。

To animate a layer property and make it “stick”, you need to change the model layer's property value, and then apply the animation.要为图层属性设置动画并使其“粘住”,您需要更改模型图层的属性值,然后应用动画。 That way, when the animation is removed, and the presentation layer goes away, the layer on screen will look exactly the same, because the model layer has the same property values as its presentation layer had when the animation ended.这样,当动画被移除并且表现层消失时,屏幕上的图层看起来将完全相同,因为模型层具有与动画结束时其表现层相同的属性值。

Now, I don't know why you're using a keyframe to animate straight-line motion, or why you're using an animation group.现在,我不知道为什么要使用关键帧来为直线运动设置动画,或者为什么要使用动画组。 Neither seems necessary here.这里似乎都没有必要。 And your two methods are virtually identical, so let's factor out the common code:并且您的两种方法实际上是相同的,因此让我们分解出通用代码:

- (void)animateLayer:(CALayer *)layer toY:(CGFloat)y {
    CGPoint fromValue = [layer.presentationLayer position];
    CGPoint toValue = CGPointMake(fromValue.x, y);

    layer.position = toValue;

    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
    animation.fromValue = [NSValue valueWithCGPoint:fromValue];
    animation.toValue = [NSValue valueWithCGPoint:toValue];
    animation.duration = 2;
    [layer addAnimation:animation forKey:animation.keyPath];
}

Notice that we're giving the animation a key when I add it to the layer.请注意,当我将动画添加到图层时,我们为动画提供了一个关键帧。 Since we use the same key every time, each new animation will replace (remove) the prior animation if the prior animation hasn't finished yet.由于我们每次都使用相同的键,如果之前的动画还没有完成,每个新动画都会替换(移除)之前的动画。

Of course, as soon as you play with this, you'll find that if you moveUp: when the moveDown: is only half finished, the moveUp: animation will appear to be at half speed because it still has a duration of 2 seconds but only half as far to travel.当然,一玩这个,你会发现如果你moveUp:moveDown:只完成一半时, moveUp:动画会出现一半速度,因为它还有2秒的持续时间,但是只有一半的路程。 We should really compute the duration based on the distance to be travelled:我们真的应该根据要行驶的距离来计算持续时间:

- (void)animateLayer:(CALayer *)layer toY:(CGFloat)y withBaseY:(CGFloat)baseY {
    CGPoint fromValue = [layer.presentationLayer position];
    CGPoint toValue = CGPointMake(fromValue.x, y);

    layer.position = toValue;

    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
    animation.fromValue = [NSValue valueWithCGPoint:fromValue];
    animation.toValue = [NSValue valueWithCGPoint:toValue];
    animation.duration = 2.0 * (toValue.y - fromValue.y) / (y - baseY);
    [layer addAnimation:animation forKey:animation.keyPath];
}

If you really need it to be a keypath animation in an animation group, your question should show us why you need those things.如果你真的需要它作为动画组中的关键路径动画,你的问题应该告诉我们为什么你需要这些东西。 Anyway, it works with those things too:无论如何,它也适用于这些东西:

- (void)animateLayer:(CALayer *)layer toY:(CGFloat)y withBaseY:(CGFloat)baseY {
    CGPoint fromValue = [layer.presentationLayer position];
    CGPoint toValue = CGPointMake(fromValue.x, y);

    layer.position = toValue;

    UIBezierPath *path = [UIBezierPath bezierPath];
    [path moveToPoint:fromValue];
    [path addLineToPoint:toValue];

    CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
    animation.path = path.CGPath;
    animation.duration =  2.0 * (toValue.y - fromValue.y) / (y - baseY);

    CAAnimationGroup *group = [CAAnimationGroup animation];
    group.duration = animation.duration;
    group.animations = @[animation];
    [layer addAnimation:group forKey:animation.keyPath];
}

You can find the full code for my test program in this gist .您可以在这个 gist 中找到我的测试程序的完整代码。 Just create a new Single View Application project and replace the contents of ViewController.m with the contents of the gist.只需创建一个新的 Single View Application 项目并将ViewController.m的内容替换为 gist 的内容即可。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM