How to identify CAAnimation within the animationDidStop delegate?

Batgar's technique is too complicated. Why not take advantage of the forKey parameter in addAnimation? It was intended for this very purpose. Just take out the call to setValue and move the key string to the addAnimation call. For example:

[[hearingAidHalo layer] addAnimation:animation forKey:@"Throb"];

Then, in your animationDidStop callback, you can do something like:

if (theAnimation == [[hearingAidHalo layer] animationForKey:@"Throb"]) ...

I just came up with an even better way to do completion code for CAAnimations:

I created a typedef for a block:

typedef void (^animationCompletionBlock)(void);

And a key that I use to add a block to an animation:

#define kAnimationCompletionBlock @"animationCompletionBlock"

Then, if I want to run animation completion code after a CAAnimation finishes, I set myself as the delegate of the animation, and add a block of code to the animation using setValue:forKey:

animationCompletionBlock theBlock = ^void(void)
{
  //Code to execute after the animation completes goes here    
};
[theAnimation setValue: theBlock forKey: kAnimationCompletionBlock];

Then, I implement an animationDidStop:finished: method, that checks for a block at the specified key and executes it if found:

- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag
{
  animationCompletionBlock theBlock = [theAnimation valueForKey: kAnimationCompletionBlock];
  if (theBlock)
    theBlock();
}

The beauty of this approach is that you can write the cleanup code in the same place where you create the animation object. Better still, since the code is a block, it has access to local variables in the enclosing scope in which it's defined. You don't have to mess with setting up userInfo dictionaries or other such nonsense, and don't have to write an ever-growing animationDidStop:finished: method that gets more and more complex as you add different kinds of animations.

Truth be told, CAAnimation should have a completion block property built into it, and system support for calling it automatically if one is specified. However, the above code gives you that same functionality with only a few lines of extra code.