簡體   English   中英

動畫期間無法獲取 CALayer 的當前位置

[英]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:時清除它或創建一個新實例,但我沒有看到您這樣做。

一個不太明顯的問題是(從您使用removedOnCompletionfillMode來看,盡管您使用了presentationLayer )您顯然不了解 Core Animation 是如何工作的。 事實證明這出奇的普遍,所以如果我錯了,請原諒我。 無論如何,請繼續閱讀,因為我將解釋 Core Animation 的工作原理以及如何解決您的問題。

在 Core Animation 中,您通常使用的圖層對象是模型圖層 當您將動畫附加到層時,Core Animation 會創建模型層的副本,稱為表示層,動畫會隨着時間的推移更改表示層的屬性。 動畫永遠不會改變模型層的屬性。

當動畫結束並(默認情況下)被移除時,表示層將被銷毀,模型層的屬性值將再次生效。 因此,屏幕上的圖層似乎“回彈”到其原始位置/顏色/任何內容。

一種常見但錯誤的解決方法是將動畫的removedOnCompletion設置為NO並將其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];
}

如果你真的需要它作為動畫組中的關鍵路徑動畫,你的問題應該告訴我們為什么你需要這些東西。 無論如何,它也適用於這些東西:

- (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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM