Animation Grouping
In the previous section, "Useful Animation Properties," we defined two particular properties that are only pertinent to animation grouping: beginTime and timeOffset. Before discussing those, however, let's consider why you might want to use an animation group rather than just adding a list of animations to the layer.
In Listing 3-12, you can see that we build up a list of basic animations and simply add them to the layer. If you want all your animations to begin at the same time and each of them have the same duration, this method is perfectly adequate.
Listing 3-12. Adding a List of Animations to the Layer
- (IBAction)animate:(id)sender; { NSRect oldRect = NSMakeRect(0.0, 0.0, 100.0, 100.0); NSRect newRect = NSMakeRect(0.0, 0.0, 300.0, 300.0); CABasicAnimation *boundsAnimation = [CABasicAnimation animationWithKeyPath:@"bounds"]; [boundsAnimation setFromValue:[NSValue valueWithRect:oldRect]]; [boundsAnimation setToValue:[NSValue valueWithRect:newRect]]; [boundsAnimation setDuration:5.0f]; CABasicAnimation *positionAnimation = [CABasicAnimation animationWithKeyPath:@"position"]; [positionAnimation setFromValue: [NSValue valueWithPoint: NSPointFromCGPoint([layer position])]]; [positionAnimation setToValue: [NSValue valueWithPoint:NSMakePoint(0.0, 0.0)]]; [positionAnimation setDuration:5.0f]; CABasicAnimation *borderWidthAnimation = [CABasicAnimation animationWithKeyPath:@"borderWidth"]; [borderWidthAnimation setFromValue:[NSNumber numberWithFloat:5.0f]]; [borderWidthAnimation setToValue:[NSNumber numberWithFloat:30.0f]]; [borderWidthAnimation setDuration:5.0f]; [layer addAnimation:boundsAnimation forKey:@"bounds"]; [layer addAnimation:positionAnimation forKey:@"position"]; [layer addAnimation:borderWidthAnimation forKey:@"borderWidth"]; }
Each animation has a duration of 5 seconds, and they begin to play back simultaneously in the next run loop and they end at the same time. The position of the layer moves to the bottom left corner, the border width grows to 30 pixels, and size of the layer grows from 100 x 100 pixels to 300 x 300 pixels.
Let's say that we would prefer that, rather than having all our animations play simultaneously, we want them to play back sequentially—one following the previous. We can achieve this by using a group animation and setting the beginTime field. I should mention now that in this case it might make more sense to use a keyframe animation instead, but you need to read Chapter 4, "Keyframe Animation," to see how that works.
We must explicitly specify the duration of our animation group so that the time for each individual animation can be split up accordingly. In our example, we set our duration of the animation group to last 15 seconds and get each of our individual animations to play back for 5 seconds. Listing 3-13 shows how we can take a previous example and instead use animation grouping for greater control over animation playback.
Listing 3-13. Using Animation Grouping
- (IBAction)animate:(id)sender; { NSRect oldRect = NSMakeRect(0.0, 0.0, 100.0, 100.0); NSRect newRect = NSMakeRect(0.0, 0.0, 300.0, 300.0); CABasicAnimation *boundsAnimation = [CABasicAnimation animationWithKeyPath:@"bounds"]; [boundsAnimation setFromValue:[NSValue valueWithRect:oldRect]]; [boundsAnimation setToValue:[NSValue valueWithRect:newRect]]; [boundsAnimation setDuration:15.0f]; [boundsAnimation setBeginTime:0.0f]; CABasicAnimation *positionAnimation = [CABasicAnimation animationWithKeyPath:@"position"]; [positionAnimation setFromValue: [NSValue valueWithPoint: NSPointFromCGPoint([layer position])]]; [positionAnimation setToValue: [NSValue valueWithPoint:NSMakePoint(0.0, 0.0)]]; [positionAnimation setDuration:15.0f]; [positionAnimation setBeginTime:5.0f]; CABasicAnimation *borderWidthAnimation = [CABasicAnimation animationWithKeyPath:@"borderWidth"]; [borderWidthAnimation setFromValue:[NSNumber numberWithFloat:5.0f]]; [borderWidthAnimation setToValue:[NSNumber numberWithFloat:30.0f]]; [borderWidthAnimation setDuration:15.0f]; [borderWidthAnimation setBeginTime:10.0f]; CAAnimationGroup *group = [CAAnimationGroup animation]; [group setDuration:15]; [group setAnimations: [NSArray arrayWithObjects:boundsAnimation, positionAnimation, borderWidthAnimation, nil]]; [layer addAnimation:group forKey:nil]; }
Notice that we have set the duration for each of the individual animations to the full fifteen seconds, but each of the animations have their begin times set to start one after the other at 0.0, 5.0, and 10.0.
You also notice that the only thing we add to the layer is the group animation. The animation objects in the group have been added with a call to –setAnimations.
You can see that there is a good bit of flexibility provided through grouping. You just need to tweak your durations and begin times to suit your needs. If you want the animations to overlap, you just change the begin times to reflect when you want them to start playing back. You want to keep your duration times all the same; otherwise each keypath value (that is, bounds, position, and borderWidth) in the layer snaps back to its original value when its duration has completed, which gives predictable, yet seemingly sporadic, behavior. Keeping all the durations the same makes them wait the full duration before they snap back. If you don't want them to snap back, you need to explicitly set their values when the animation finishes, which we previously discussed in the section, "Using CABasicAnimation."