Search code examples
ioscore-animation

Chaining the animation of a layer and its sublayer (sublayer model effects superlayer animation)


I'm trying to chain the animation of a layer and its sublayer. However, the problem I'm having is that the model update of the sublayer's position for its animation is visually apparent during its superlayer's animation. Here's the code I'm using to chain animations:

// Superlayer. 
CFTimeInterval now = [self.artworkContainer.layer convertTime:CACurrentMediaTime() fromLayer:nil];
CABasicAnimation *slideDown = [CABasicAnimation animationWithKeyPath:@"position"];
slideDown.duration = SLIDE_DOWN_DURATION; //SLIDE_DOWN_DURATION;
slideDown.beginTime = now;
slideDown.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
slideDown.fromValue = [self.artworkContainer.layer valueForKey:@"position"];

CGPoint finalPointOfContainer = CGPointMake(self.artworkContainer.layer.position.x, self.artworkContainer.layer.position.y+verticalDistanceOfFirstFall);
slideDown.toValue = [NSValue valueWithCGPoint:finalPointOfContainer];
self.artworkContainer.layer.position = finalPointOfContainer;
[self.artworkContainer.layer addAnimation:slideDown forKey:@"position"];

// Sublayer 
CABasicAnimation *moveActualArtwork = [CABasicAnimation animationWithKeyPath:@"position"];
moveActualArtwork.duration = timeOfSecondFall;
moveActualArtwork.beginTime = now + SLIDE_DOWN_DURATION;
moveActualArtwork.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
moveActualArtwork.fromValue = [self.artWork.layer valueForKey:@"position"];

CGPoint finalPointOfArtwork = CGPointMake(self.artWork.layer.position.x, self.artWork.layer.position.y+verticalDistanceOfSecondFall);
moveActualArtwork.toValue = [NSValue valueWithCGPoint:finalPointOfArtwork];
self.artWork.layer.position = finalPointOfArtwork; // If this is commented out, the superlayer animation looks correct (of course this isn't a true fix because the sublayer snaps back to its original position without a model update). 

moveActualArtwork.delegate = self;
[self.artWork.layer addAnimation:moveActualArtwork forKey:@"position"];

Solution

  • I found the answer as I was writing the question. I needed to set the fillMode property of the second animation: moveActualArtwork.fillMode = kCAFillModeBackwards;

    If one were to think of the fromValue and toValue as positions on a timeline, the problem was that before the sublayer hit the fromValue spot on the timeline, it was working off of its model layer to determine its position. By using kCAFillModeBackwards, the fromValue is effectively extended to cover the timeline previous to it so iOS knows what to render before the animation actually begins.

    I found the answer reviewing the WWDC 2011 video Session 421 - Core Animation Essentials, at 45:31.