我正在尝试制作一个动画,当你按住一个按钮时它会向下动画一个块,当你释放时,它会将它动画回原来的位置,但我无法获得动画块的当前位置有什么关系。这是我的代码:
-(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];
仅返回目标位置而不是当前位置。我需要基本上给我一个容器的当前位置,一旦我释放按钮动画,所以我可以执行下一个动画。我现在所做的不起作用。
答案 0 :(得分:44)
我没有足够的分析您的代码,以至于100%确定为什么[[container.layer presentationLayer] frame]
可能无法返回您的期望。但我看到了几个问题。
一个明显的问题是moveDown:
未声明movePath
。如果movePath
是一个实例变量,您可能希望每次调用moveDown:
时清除它或创建一个新实例,但我不认为您这样做。
一个不太明显的问题是(根据您对removedOnCompletion
和fillMode
的使用情况,尽管您使用了presentationLayer
),但您显然无法理解Core Animation的工作原理。事实证明这是非常普遍的,所以如果我错了,请原谅我。请继续阅读,因为我将解释Core Animation的工作原理以及如何解决您的问题。
在核心动画中,您通常使用的图层对象是模型图层。将动画附加到图层时,Core Animation会创建模型图层的副本,称为表示层,动画会随时间更改表示层的属性。动画永远不会更改模型图层的属性。
当动画结束并且(默认情况下)被删除时,表示层将被销毁,模型图层属性的值将再次生效。因此,屏幕上的图层似乎会“快速恢复”到其原始位置/颜色/等等。
解决此问题的一种常见但错误的方法是将动画的removedOnCompletion
设置为NO
,将fillMode
设置为kCAFillModeForwards
。执行此操作时,表示层会挂起,因此屏幕上没有“快照”。问题是,现在您的表示层与模型层的值不同。如果您询问模型图层(或拥有它的视图)的动画属性值,您将获得一个与屏幕上的值不同的值。如果您尝试再次为该属性设置动画,则动画可能会从错误的位置开始。
要为图层属性设置动画并使其“粘贴”,您需要更改模型图层的属性值,然后应用动画。这样,当移除动画并且表示层消失时,屏幕上的图层看起来将完全相同,因为模型图层具有与动画结束时表示层相同的属性值。
现在,我不知道你为什么使用关键帧为直线运动设置动画,或者为什么要使用动画组。这里似乎没有必要。而且你的两种方法实际上是相同的,所以让我们分解出常见的代码:
- (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];
}
如果您真的需要它作为动画组中的键路径动画,那么您的问题应该向我们展示为什么您需要这些东西。无论如何,它也适用于那些事情:
- (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
的内容替换为要点的内容。