Search code examples
objective-ccocoa-touchcore-animationcalayer

CALayer fade from current value


My app uses CALayer to draw views. More precisely, it uses the drawLayer:inContext: method on a sublayer of a UIView's top layer. This is a nice way to get the 'implicit' animation of consecutive drawLayer:inContext: drawings to fade into each other over time. The fading animations happen fairly fast, maybe in 0.25 seconds, but to change its duration, simply implement another delegate method called actionForLayer:forKey:. In this perfectly working example implementation here the default duration is stretched to 2.0 seconds:

- (id<CAAction>)actionForLayer:(CALayer *)layer forKey:(NSString *)event
{
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:event];
    animation.duration = 2.0;
    return animation;

    // or return nil for the default duration.
}

On to the issue at hand.

If you call [sublayer setNeedsDisplay] faster than the fades have time to complete, with each new fade you'll see a sudden jump. From the looks of it, the fade that's in progress is cancelled and it's final state is used as the starting point of the new fade. This might not be very surprising, but the visual result is rather unwanted.

Consider the scenario of a ten second fade from black to white, with another fade, to black, triggered five seconds after the start. The animation will start fading from black to white, but when it's at a 'half way gray' it jumps to full white before fading to black again.

Is there a way to prevent this from happening? Can I get the layer to fade from the gray back down to black? Is there a CALayer drawing equivalent of saying UIViewAnimationOptionBeginFromCurrentState (used in UIView animations)?

Cheers.


Solution

  • A layer's animation is only a visual representation of what the layer should look like as it animates. In CA when you animate from one state to another, the entire state of the layer changes immediately. A presentation layer is created and displays the animation, and when the animation completes the actual layer is left in place at the end.

    So, my guess is that when you want to transition from one state to another, and the current animation hasn't completed yet, you have to capture the current state of the animation and then use this as the starting point for your next animation.

    The problem lies in not being able to modify a layer's current animation.

    In the following post I capture the current state of an animation, set that as the current state for the layer and use that as the beginning value from which to animate. The post applies this technique to the speed / duration of an animation, but can also be applied to your scenario.

    https://stackoverflow.com/a/9544674/1218605