views:

461

answers:

2

Core Animation allows for custom animations by implementing the actionForKey method in your CALayer based class:

- (id<CAAction>)actionForKey:(NSString *)key {
 // Custom animations
 return [super actionForKey:key];
}

I can then create an animation and return it for the onOrderIn action (i.e. when the layer is added to another layer). This works just fine. If I do the same for onOrderOut (i.e. the layer is removed from its superlayer), the returned animation is ignored, and the default animation is applied instead.

My goal is to zoom the layer in (onOrderIn) and out (onOrderOut):

- (id<CAAction>)actionForKey:(NSString *)key {

 if ([key isEqualToString:@"onOrderIn"] || [key isEqualToString:@"onOrderOut"]) {
  CABasicAnimation *a = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
  a.duration = 0.25;
  a.removedOnCompletion = NO;
  a.fillMode = kCAFillModeBoth;

  if ([key isEqualToString:@"onOrderIn"]) {
   a.fromValue = [NSNumber numberWithFloat:0.0];
   a.toValue = [NSNumber numberWithFloat:1.0];
  } else {
   a.fromValue = [NSNumber numberWithFloat:1.0];
   a.toValue = [NSNumber numberWithFloat:0.0];
  }

  return a;
 }

 return [super actionForKey:key];
}

Zooming in works, zooming out does not. Instead the default fade out animation is used.

The code might contain some typos, as I'm typing this on another machine.

Can anyone help?

A: 

Have you verified that your method is being called with key as @"onOrderOut" and that your method is returning the correct animation?

Colin Barrett
Yes. In the actual code I first call NSLog with the name of the action and indeed, onOrderOut is requested. I also made sure that the animation is created and returned, but still, it just gets ignored.
Lemming
+3  A: 

Quoting John Harper on quartz-dev mailing list:

There's a fundamental problem with returning any animation for the onOrderOut key—by the time the animation should be running, the layer is no longer in the tree, so it has no effect. So onOrderOut is not useful for triggering animations; it may be useful for running other code when layers are removed from the tree.

The best solution I've found for this (assuming the default fade transition on the parent is not what you want, which it often isn't) is to add custom animations to apply the removal effect you want, then, in the didStop animation delegate, actually remove the layer. It's often convenient to create a single group of animations with the delegate property set, and fillMode=forwards, removedOnCompletion=NO so that you can remove the layer at the end of the animation with no possibility of the layer still being visible in its normal state.

If you do many case of this, it is easy to write a common superclass that starts an animation, sets the animation delegate to the class and implements +animationDidStop: to remove the layer w/o animation enabled. This restores the fire-and-forget nature of CoreAnimation that you'd have hoped would be present with the default implementation.

tjw
Ah, thank you. I will test this should I need it again, in the mean while we wrote an entirely different version of the software with Qt Jambi. ;)
Lemming