动画期间无法获取CALayer的当前位置 [英] Cannot get current position of CALayer during animation

查看:192
本文介绍了动画期间无法获取CALayer的当前位置的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试制作一个动画,当你按住一个按钮时它会动画一个块,当你释放时,它会将它动画回原来的位置,但是我无法获得动画块的当前位置有什么关系。这是我的代码:

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

}

但行

   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.

推荐答案

我没有足够的分析你的代码以100%确定为什么 [[container.layer presentationLayer] frame] 可能无法返回您的预期。但是我看到了几个问题。

I haven't analyzed your code enough to be 100% sure why [[container.layer presentationLayer] frame] might not return what you expect. But I see several problems.

一个明显的问题是 moveDown:没有声明 movePath 。如果 movePath 是一个实例变量,您可能希望每次调用 moveDown:时清除它或创建一个新实例,但我不认为你这样做。

One obvious problem is that moveDown: doesn't declare 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.

一个不太明显的问题是(根据你使用 removedOnCompletion fillMode ,尽管你使用了 presentationLayer ,但你显然不明白Core Animation是如何工作的。事实证明这是非常普遍的,如果我错了,请原谅我。无论如何,请继续阅读,因为我将解释Core Animation的工作方式以及如何解决您的问题。

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. 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中,您通常使用的图层对象是模型层。将动画附加到图层时,Core Animation会创建模型图层的副本,称为表示层,动画会随时间更改表示层的属性。动画永远不会更改模型图层的属性。

In Core Animation, the layer object you normally work with is a model layer. 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. 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.

一种常见的,但错误的修复方式这是将动画的 removedOnCompletion 设置为 NO 及其 fillMode kCAFillModeForwards 。执行此操作时,表示层会挂起,因此屏幕上没有快照。问题是,现在您的表示层与模型层的值不同。如果您要求模型图层(或拥有它的视图)获取动画属性的值,您将获得一个与屏幕上的值不同的值。如果您尝试再次为该属性设置动画,则动画可能会从错误的位置开始。

A common, but wrong way to fix this is to set the animation's removedOnCompletion to NO and its fillMode to kCAFillModeForwards. 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.

当然,尽快当你玩这个时,你会发现如果 moveUp: moveDown:只完成了一半时, moveUp:动画将显示为半速,因为它仍然有2秒的持续时间,但只有一半的距离。我们应该根据要旅行的距离来计算持续时间:

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

如果真的需要它是一个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];
}

您可以找到我的测试程序的完整代码在这个要点。只需创建一个新的单视图应用程序项目,并用要点的内容替换 ViewController.m 的内容。

You can find the full code for my test program in this gist. Just create a new Single View Application project and replace the contents of ViewController.m with the contents of the gist.

这篇关于动画期间无法获取CALayer的当前位置的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆