2012-12-15 38 views
12

我想要实现一个动画,当您按住按钮时,它将动画块放下,当您释放时,它将动画恢复到原始位置,但我无法获得无论如何,动画块的当前位置。这里是我的代码:在动画过程中无法获取CALayer的当前位置

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

只返回目的地位置不是当前位置。我需要基本上给我容器的当前位置,当我释放按钮时,它就会动画,所以我可以执行下一个动画。我现在有什么不行。

回答

43

我还没有分析你的代码,足以100%确定为什么[[container.layer presentationLayer] frame]可能不会返回你所期望的。但我看到几个问题。

一个明显的问题是moveDown:未声明movePath。如果movePath是一个实例变量,那么您可能需要在每次调用moveDown:时清除它或创建一个新实例,但我没有看到您这样做。

一个不太明显的问题是(从使用removedOnCompletionfillMode来判断,尽管你使用了presentationLayer),你显然不明白Core Animation是如何工作的。事实证明这很常见,所以如果我错了,请原谅我。无论如何,请继续阅读,因为我将解释Core Animation的工作原理以及如何解决您的问题。

在Core Animation中,您通常使用的图层对象是模型图层。将动画附加到图层时,Core Animation会创建模型图层的副本,称为表示层,动画会随着时间的推移更改表示层的属性。动画不会更改模型图层的属性。

当动画结束并且(默认情况下)被删除时,表示层被销毁,模型图层属性的值再次生效。因此,屏幕上的图层显示为“回弹”到其原始位置/颜色/无论如何。

常见的,但解决这个错误方法是设置动画的removedOnCompletionNO及其fillModekCAFillModeForwards。当你这样做的时候,表示层会挂起,所以屏幕上没有“回弹”。现在的问题是,您现在将表示层与模型图层挂在不同的值上。如果您向动画属性的值询问模型图层(或拥有它的视图),则会获得与屏幕上显示的值不同的值。如果您尝试再次为该属性制作动画,动画可能会从错误的地方开始。

要为图层属性设置动画并使其“粘住”,您需要更改模型图层的属性值,然后应用动画。这样,当动画被移除,并且表示层消失时,屏幕上的图层将看起来完全相同,因为模型图层具有与动画结束时的表示层相同的属性值。

现在,我不知道为什么要使用关键帧来为直线运动设置动画,或者为什么要使用动画组。这两个都不是必要的。而你的两种方法实际上是一致的,所以让我们分解出公共代码:

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

请注意,我们给了动画的关键,当我把它添加到层。由于我们每次都使用相同的密钥,因此如果先前的动画尚未完成,每个新的动画将替换(移除)先前的动画。

当然,只要你玩这个,你会发现,如果你moveUp:moveDown:只完成了一半,在moveUp:动画会显得速度的一半,因为它仍然有2秒的持续时间但旅行的距离只有一半。我们真的应该计算基于距离的时间要行进:

- (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动画,你的问题应该向我们展示为什么你需要这些东西。无论如何,它与那些东西太多:

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

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

+0

非常感谢。我是使用动画的新手,这帮助了很多!我会给它一个镜头! – Matt

+4

观看WWDC 2011上的[Session 421 - Core Animation Essentials](https://developer.apple.com/videos/wwdc/2011/?id=421)视频。它只有一个小时,非常**翔实。 –

+0

真棒回答+1! – Till