按顺序运行 CABasicAnimation

问题描述 投票:0回答:6

如何让一个 CABasicAnimation 在另一个 CABasicAnimation 完成后运行?换句话说,顺序。我已经添加了第二个动画的开始时间,但是似乎第二个动画没有被执行:

CABasicAnimation * appearance =[CABasicAnimation animationWithKeyPath:@"transform.translation.y"];
    appearance.duration = 0.5;
    appearance.fromValue = [NSNumber numberWithFloat:0];
    appearance.toValue = [NSNumber numberWithFloat:340];
    appearance.repeatCount = 1;
    appearance.fillMode = kCAFillModeForwards;
    appearance.removedOnCompletion = NO;
    [notif.layer addAnimation:appearance forKey:@"transform.translation.y"];



    CABasicAnimation * theAnimation=[CABasicAnimation animationWithKeyPath:@"transform.translation.y"];
    theAnimation.duration = 0.5;
    theAnimation.fromValue = [NSNumber numberWithFloat:0];
    theAnimation.toValue = [NSNumber numberWithFloat:10];
    theAnimation.repeatCount = 3;
    theAnimation.autoreverses = YES;
    theAnimation.fillMode = kCAFillModeForwards;
    theAnimation.removedOnCompletion = NO;
    theAnimation.beginTime = appearance.beginTime + appearance.duration;
    [notif.layer addAnimation:theAnimation forKey:@"transform.translation.y"];

知道为什么吗?

iphone objective-c ios core-animation cabasicanimation
6个回答
15
投票

更新代码,这会起作用!

第一部动画

CABasicAnimation * appearance =[CABasicAnimation animationWithKeyPath:@"transform.translation.y"];
    [appearance setValue:@"animation1" forKey:@"id"];
    appearance.delegate = self;
    [UIView setAnimationDidStopSelector:@selector(animationDidStop:finished:)];
    appearance.duration = 0.5;
    appearance.fromValue = [NSNumber numberWithFloat:0];
    appearance.toValue = [NSNumber numberWithFloat:340];
    appearance.repeatCount = 1;
    appearance.fillMode = kCAFillModeForwards;
    appearance.removedOnCompletion = NO;
    [notif.layer addAnimation:appearance forKey:@"transform.translation.y"];

第二个动画:

    - (void)animationDidStop:(CAAnimation *)theAnimation2 finished:(BOOL)flag {
    if([[theAnimation2 valueForKey:@"id"] isEqual:@"animation1"]) {
    CABasicAnimation * theAnimation=[CABasicAnimation animationWithKeyPath:@"transform.translation.y"];
    theAnimation.duration = 0.5;
    theAnimation.fromValue = [NSNumber numberWithFloat:0];
    theAnimation.toValue = [NSNumber numberWithFloat:10];
    theAnimation.repeatCount = 3;
    theAnimation.autoreverses = YES;
    theAnimation.fillMode = kCAFillModeForwards;
    theAnimation.removedOnCompletion = NO;
    theAnimation.beginTime = appearance.beginTime + appearance.duration;
    [notif.layer addAnimation:theAnimation forKey:@"transform.translation.y"];
}

}

这就是第一个动画完成后第二个动画的触发方式。


2
投票

最简单的事情是为第一个动画设置一个委托,并在该对象中实现

-animationDidStop:finished:
。在那个方法中,触发第二个动画。


1
投票

你的动画没有按计划运行的原因是你编码和使用 Core Animation 的方式。核心动画全部由 GPU 驱动。事实上,CA 可以很好地处理几件事以最佳地渲染动画。

它的一个要素是现代显卡的原始并行处理能力,另一个是 Core Animation 可以在卡上缓存 CALayer 的内容,这样您的代码就不需要不断地重绘它。顺便说一下,这也是 iPhone 用户界面在一些相对适中的硬件上运行速度如此之快的部分原因。 Core Animation 可以自动利用多核 Mac,因为图层树是在 单独的线程 上渲染的。

最后一行是imp。 CA 在单独的线程中运行(而不是主线程)。所以回到你的问题,你有 2 个 CA 块,每个块都试图同时为相同的

transform.translation.y
制作动画!这将如何工作?

所以要解决这个问题,要么做-

  1. 有人建议,延迟第二个动画,以便在第一个动画之后,第二个仅在第一个操作系统结束后才开始。
  2. 或尝试在一个块中完成整个动画(再次按照某人的建议)。

我只是想强调 Core Animation 工作方式背后的理论和推理。希望这有助于...


0
投票

真的就这么简单...

let basic_seconds = 2.0

blah.path = .. begin

let a1 = CABasicAnimation(keyPath: "path")
.. set from, to, etc
a1.duration = basic_seconds

let a2 = CABasicAnimation(keyPath: "path")
.. set from, to, etc
a2.beginTime = CACurrentMediaTime() + basic_seconds
a2.duration = 0.5

blah.add(a1, forKey: "a1")
blah.add(a2, forKey: "a2") // KEYS MUST BE DIFFERENT

CATransaction.begin()
blah.path = .. end 
CATransaction.commit()

你只需要运行两个,但将第二个的 DELAY 设置为第一个的长度。

注意在罕见的复杂情况下(你不知道第一个的长度),使用

-animationDidStop:finished:
@ChrisParker points out.

不要忘记在 CA 动画中设置延迟,您只需使用这个愚蠢的代码:

 .beginTime = CACurrentMediaTime() + .. some delay seconds ..

这很奇怪,但这是执行顺序 CA 动画的唯一机制。


-1
投票

iOS 15、斯威夫特 5.x

有点疯狂,但这是我可以开始工作的唯一解决方案,我尝试的所有其他事情都想并行进行。

请注意,当第一个完成时,它不会运行第二个,它在运行第二个之前等待 4 秒,因为它知道第一个需要 4 秒。

let foo = SCNAction.customAction(duration: 0) { (node,elapsedTime) in
  let animation = CABasicAnimation(keyPath:"morpher.weights[0]")
  animation.fromValue = 0.0
  animation.toValue = 1.0
  animation.autoreverses = true
  animation.repeatCount = 0
  animation.isRemovedOnCompletion = true
  animation.fillMode = .removed
  animation.duration = 2
  node.addAnimation(animation, forKey: nil)   
}

let delay = SCNAction.wait(duration: 4)
    
let foo2 = SCNAction.customAction(duration: 0) { (node,elapsedTime) in
  let animation2 =  CABasicAnimation(keyPath:"morpher.weights[1]")
  animation2.fromValue = 0.0
  animation2.toValue = 1.0
  animation2.autoreverses = true
  animation2.repeatCount = 0
  animation2.isRemovedOnCompletion = true
  animation2.fillMode = .removed
  animation2.duration = 2
  node.addAnimation(animation2, forKey: nil)
}
    
node.runAction(SCNAction.sequence([foo,delay,foo2]))

-2
投票

最简单和最干净的方法是使用

CAAnimationGroup
。看看这个答案 - 它帮助了我。

链接核心动画动画

© www.soinside.com 2019 - 2024. All rights reserved.