views:

3890

answers:

7

I had a problem where I had a series of overlapping CATransition / CAAnimation sequences, all of which I needed to perform custom operations when the animations stopped, but I only wanted one delegate handler for animationDidStop.

However, I had a problem, there didn't appear to be a way to uniquely identify each CATransition / CAAnimation in the animationDidStop delegate.

I solved this problem via the key / value system exposed as part of CAAnimation.

When you start your animation use the setValue method on the CATransition / CAAnimation to set your identifiers and values to use when animationDidStop fires:

-(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];
    

    }

In your animationDidStop delegate:

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

The other aspect of this is that it allows you to keep state in the key value pairing system instead of having to store it in your delegate class. The less code, the better.

Be sure to check out the Apple Reference on Key Value Pair Coding.

Are there better techniques for CAAnimation / CATransition identification in the animationDidStop delegate?

Thanks, --Batgar

A: 

IMHO using Apple's key-value is the elegant way of doing this: it's specifically meant to allow adding application specific data to objects.

Other much less elegant possibility is to store references to your animation objects and do a pointer comparision to identify them.

tequilatango
A: 

Batgar,

When I googled for "iphone animationDidStop identify", the first hit was your post, suggesting the use of key-value to identify the animation. Just what I needed, thank you.

Rudi

rudifa
+8  A: 

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"]) ...
vocaro
That is slick, and way easier, thanks trevor.
Batgar
+1  A: 

Hi Guys,

I have just tried both these approaches and neither seems to work for me. If I go for Batgar's option and grabbing the animation name (key value) to put into a string, I'm told that the value passed is not a CFString and the NSString remains nil at the comparison stage.

If I try the suggestion by Trevor the key/value comparison never matches and so my if statement code is never true. Does anyone have any ideas why this would be please? Code is below.

Thanks,

Gareth.

// Add the animation to the layer [[self.whoScoredLabel layer] addAnimation:positionAnimation forKey:@"animateOutWhoScoredLabelToSide"];

if (theAnimation == [[whoScoredLabel layer] animationForKey:@"animateOutWhoScoredLabelToSide"]) { // Never true!

    // Who scored text has been animated out so safe to release
    self.whoScoredLabel = nil;      

}

Gareth
A: 

Hi Friends, I tired to add the CAAnimationDelegate in my view controller and after compilation I got the following error:

cannot find protocol declaration for 'CAAnimationDelegate' .

Can anyone suggest a way to get rid of it?

Regards, krishnan.

Krishnan
A: 

Batgar,excellent!

wilmer
+2  A: 

The second approach will only work if you explicitly set your animation to not be removed on completion before running it:

CAAnimation *anim = ...
anim.removedOnCompletion = NO;

If you fail to do so, your animation will get removed before when it completes, and the callback will not find it in the dictionary.

jimt