如何让一个 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"];
知道为什么吗?
更新代码,这会起作用!
第一部动画
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"];
}
}
这就是第一个动画完成后第二个动画的触发方式。
最简单的事情是为第一个动画设置一个委托,并在该对象中实现
-animationDidStop:finished:
。在那个方法中,触发第二个动画。
你的动画没有按计划运行的原因是你编码和使用 Core Animation 的方式。核心动画全部由 GPU 驱动。事实上,CA 可以很好地处理几件事以最佳地渲染动画。
它的一个要素是现代显卡的原始并行处理能力,另一个是 Core Animation 可以在卡上缓存 CALayer 的内容,这样您的代码就不需要不断地重绘它。顺便说一下,这也是 iPhone 用户界面在一些相对适中的硬件上运行速度如此之快的部分原因。 Core Animation 可以自动利用多核 Mac,因为图层树是在 单独的线程 上渲染的。
最后一行是imp。 CA 在单独的线程中运行(而不是主线程)。所以回到你的问题,你有 2 个 CA 块,每个块都试图同时为相同的
transform.translation.y
制作动画!这将如何工作?
所以要解决这个问题,要么做-
我只是想强调 Core Animation 工作方式背后的理论和推理。希望这有助于...
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 动画的唯一机制。
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]))