如何识别animationDidStop代表中的CAAnimation?
我有一个问题,我有一系列重叠的CATransition / CAAnimation序列,当animation停止时,所有这些都需要执行自定义操作,但是我只需要一个用于animationDidStop的委托处理程序。
但是,我遇到了一个问题,似乎没有办法唯一标识animationDidStop委托中的每个CATransition / CAAnimation。
我通过作为CAAnimation一部分公开的键/值系统解决了这个问题。
当您开始animation时,使用CATransition / CAAnimation上的setValue方法来设置animationDidStop触发时要使用的标识符和值:
-(void)volumeControlFadeToOrange { CATransition* volumeControlAnimation = [CATransition animation]; [volumeControlAnimation setType:kCATransitionFade]; [volumeControlAnimation setSubtype:kCATransitionFromTop]; [volumeControlAnimation setDelegate:self]; [volumeControlLevel setBackgroundImage:[UIImage imageNamed:@"SpecialVolume1.png"] forState:UIControlStateNormal]; volumeControlLevel.enabled = true; [volumeControlAnimation setDuration:0.7]; [volumeControlAnimation setValue:@"Special1" forKey:@"MyAnimationType"]; [[volumeControlLevel layer] addAnimation:volumeControlAnimation forKey:nil]; } - (void)throbUp { doThrobUp = true; CATransition *animation = [CATransition animation]; [animation setType:kCATransitionFade]; [animation setSubtype:kCATransitionFromTop]; [animation setDelegate:self]; [hearingAidHalo setBackgroundImage:[UIImage imageNamed:@"m13_grayglow.png"] forState:UIControlStateNormal]; [animation setDuration:2.0]; [animation setValue:@"Throb" forKey:@"MyAnimationType"]; [[hearingAidHalo layer] addAnimation:animation forKey:nil]; }
在您的animationDidStop委托中:
- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag{ NSString* value = [theAnimation valueForKey:@"MyAnimationType"]; if ([value isEqualToString:@"Throb"]) { //... Your code here ... return; } if ([value isEqualToString:@"Special1"]) { //... Your code here ... return; } //Add any future keyed animation operations when the animations are stopped. }
另一方面,它允许你在关键值配对系统中保持状态,而不必将它存储在委托类中。 代码越less越好。
请务必查看关键值对编码的Apple参考 。
animationDidStop委托中有更好的CAAnimation / CATransition标识技术吗?
谢谢,巴特加
巴特加的技巧太复杂了。 为什么不利用addAnimation中的forKey参数? 这是为了这个目的。 只需调用setValue并将键string移动到addAnimation调用。 例如:
[[hearingAidHalo layer] addAnimation:animation forKey:@"Throb"];
然后,在您的animationDidStopcallback中,您可以执行如下操作:
if (theAnimation == [[hearingAidHalo layer] animationForKey:@"Throb"]) ...
我刚刚想出了一个更好的方法来完成CAAnimations的代码:
我为一个块创build了一个typedef:
typedef void (^animationCompletionBlock)(void);
还有一个我用来为animation添加块的关键字:
#define kAnimationCompletionBlock @"animationCompletionBlock"
然后,如果我想在CAAnimation完成后运行animation完成代码,我将自己设置为animation的委托,并使用setValue:forKey将一段代码添加到animation中:
animationCompletionBlock theBlock = ^void(void) { //Code to execute after the animation completes goes here }; [theAnimation setValue: theBlock forKey: kAnimationCompletionBlock];
然后,我实现了一个animationDidStop:finished:方法,该方法检查指定键上的块,如果find则执行它:
- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag { animationCompletionBlock theBlock = [theAnimation valueForKey: kAnimationCompletionBlock]; if (theBlock) theBlock(); }
这种方法的美妙之处在于,您可以在创buildanimation对象的相同位置编写清理代码。 更好的是,由于代码是一个块,它可以访问它所在的封闭范围内的局部variables。 您不必费心设置userInfo字典或其他类似的废话,也不必编写不断增长的animationDidStop:finished:方法,在添加不同types的animation时,它会变得越来越复杂。
事实上,CAAnimation应该有一个内置的完成块属性,并且系统支持在指定的时候自动调用它。 但是,上面的代码只给出了几行额外代码的相同function。
第二种方法只有在你明确地设置你的animation在完成之前不能被删除之前,
CAAnimation *anim = ... anim.removedOnCompletion = NO;
如果你不这样做,你的animation将在它完成之前被删除,callback将不会在字典中find它。
所有其他的答案太复杂了! 你为什么不添加自己的密钥来识别animation?
这个解决scheme非常简单,只需要将您自己的密钥添加到animation (在本例中为animationID )
插入这一行来标识animation1 :
[myAnimation1 setValue:@"animation1" forKey:@"animationID"];
这个来识别animation2 :
[myAnimation2 setValue:@"animation2" forKey:@"animationID"];
像这样testing它:
- (void)animationDidStop:(CAAnimation *)animation finished:(BOOL)flag { if([[animation valueForKey:@"animationID"] isEqual:@"animation1"]) { //animation is animation1 } else if([[animation valueForKey:@"animationID"] isEqual:@"animation2"]) { //animation is animation2 } else { //something else } }
它不需要任何实例variables :
为了明确上面所暗示的(以及在浪费了几个小时后让我来到这里的是什么):不要期望看到你分配给你的原始animation对象
- (void)animationDidStop:(CAAnimation*)animation finished:(BOOL)flag
当animation结束时,因为[CALayer addAnimation:forKey:]
制作了一个animation副本。
你可以依赖的是,你赋予animation对象的键值仍然存在于与animationDidStop:finished:
消息传递的副本animation对象中的等价值(但不一定是指针等价)。 如上所述,使用KVC,您可以获得足够的范围来存储和检索状态。
恕我直言,使用苹果的键值是这样做的优雅的方式:它的具体意思是允许添加应用程序特定的数据对象。
其他更不优雅的可能性是存储对您的animation对象的引用,并做一个指针比较来识别它们。
对于我来检查2 CABasicAnimation对象是否是相同的animation,我使用keyPath函数来做到这一点。
if([animationA keyPath] == [animationB keyPath])
- 没有必要为CABasicAnimation设置KeyPath,因为它不再具有animation效果
我可以看到大多数objc的答案,我会根据上面的最佳答案做一个快速2.3。
一开始,将所有这些密钥存储在一个私有结构中是很好的,所以它是types安全的,在将来改变它不会因为你忘记在代码中的任何地方改变它而给你带来烦人的错误:
private struct AnimationKeys { static let animationType = "animationType" static let volumeControl = "volumeControl" static let throbUp = "throbUp" }
正如你所看到的,我已经改变了variables/animation的名字,所以更清楚。 现在在创buildanimation时设置这些键。
volumeControlAnimation.setValue(AnimationKeys.volumeControl, forKey: AnimationKeys.animationType)
(……)
throbUpAnimation.setValue(AnimationKeys.throbUp, forKey: AnimationKeys.animationType)
然后最后处理animation停止时的委托
override func animationDidStop(anim: CAAnimation, finished flag: Bool) { if let value = anim.valueForKey(AnimationKeys.animationType) as? String { if value == AnimationKeys.volumeControl { //Do volumeControl handling } else if value == AnimationKeys.throbUp { //Do throbUp handling } } }
Xcode 9 Swift 4.0
您可以使用键值将您添加到animationDidStop委托方法中返回的animation的animation相关联。
声明一个字典来包含所有活动的animation和相关的完成:
var animationId: Int = 1 var animating: [Int : () -> Void] = [:]
当您添加animation时,请为其设置一个键:
moveAndResizeAnimation.setValue(animationId, forKey: "CompletionId") animating[animationId] = { print("completion of moveAndResize animation") } animationId += 1
在animationDidStop中,魔术发生了:
let animObject = anim as NSObject if let keyValue = animObject.value(forKey: "CompletionId") as? Int { if let completion = animating.removeValue(forKey: keyValue) { completion() } }
我喜欢使用setValue:forKey
:来保存视图的引用,我比较喜欢animation,因为同一种animation可以添加到不同的图层,所以比试图唯一标识基于ID的animation更安全。
这两个是等价的:
[UIView animateWithDuration: 0.35 animations: ^{ myLabel.alpha = 0; } completion: ^(BOOL finished) { [myLabel removeFromSuperview]; }];
与这一个:
CABasicAnimation *fadeOut = [CABasicAnimation animationWithKeyPath:@"opacity"]; fadeOut.fromValue = @([myLabel.layer opacity]); fadeOut.toValue = @(0.0); fadeOut.duration = 0.35; fadeOut.fillMode = kCAFillModeForwards; [fadeOut setValue:myLabel forKey:@"item"]; // Keep a reference to myLabel fadeOut.delegate = self; [myLabel.layer addAnimation:fadeOut forKey:@"fadeOut"]; myLabel.layer.opacity = 0;
并在委托方法中:
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag { id item = [anim valueForKey:@"item"]; if ([item isKindOfClass:[UIView class]]) { // Here you can identify the view by tag, class type // or simply compare it with a member object [(UIView *)item removeFromSuperview]; } }