[英]Cannot get current position of CALayer during animation
我試圖實現一個動畫,當你按住一個按鈕時,它會向下移動一個塊,當你釋放時,它會讓它回到原始位置,但無論如何我都無法獲得動畫塊的當前位置。 這是我的代碼:
-(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];
只返回目標位置而不是當前位置。 一旦我釋放按鈕,我基本上需要給我動畫容器的當前位置,以便我可以執行下一個動畫。 我現在所擁有的不起作用。
我還沒有對您的代碼進行足夠的分析,無法 100% 確定為什么[[container.layer presentationLayer] frame]
可能不會返回您期望的內容。 但我看到了幾個問題。
一個明顯的問題是moveDown:
沒有聲明movePath
。 如果movePath
是一個實例變量,您可能希望在每次調用moveDown:
時清除它或創建一個新實例,但我沒有看到您這樣做。
一個不太明顯的問題是(從您使用removedOnCompletion
和fillMode
來看,盡管您使用了presentationLayer
)您顯然不了解 Core Animation 是如何工作的。 事實證明這出奇的普遍,所以如果我錯了,請原諒我。 無論如何,請繼續閱讀,因為我將解釋 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];
}
您可以在這個 gist 中找到我的測試程序的完整代碼。 只需創建一個新的 Single View Application 項目並將ViewController.m
的內容替換為 gist 的內容即可。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.