Bronze Solution


#1

The book gives an example of using a CABasicAnimation to fade a layer’s opacity over a period of 2 seconds. You could create the same CABasicAnimation and use a CAAnimationGroup combining the bounce and fader animations. However, the Bronze challenge goes a step further and asks you to match the fading with the shrinking and growing of the label.

My solution involves creating a CAKeyframeAnimation with values that correspond to the different bounce values and then use a CAAnimationGroup. I used the scale from each of the CATransform3D objects and adjusted the values so they are within the range of 0.0 to 1.0.

TimeViewController.m

- (void)bounceTimeLabel
{
    //create a key frame animation
    CAKeyframeAnimation *bounce = [CAKeyframeAnimation animationWithKeyPath:@"transform"];
    
    //create the values it will pass through
    CATransform3D forward = CATransform3DMakeScale(1.3, 1.3, 1);
    CATransform3D back = CATransform3DMakeScale(0.7, 0.7, 1);
    CATransform3D forward2 = CATransform3DMakeScale(1.2, 1.2, 1);
    CATransform3D back2 = CATransform3DMakeScale(0.9, 0.9, 1);
    
    NSArray *bounceValues = [NSArray arrayWithObjects:[NSValue valueWithCATransform3D:CATransform3DIdentity],
                            [NSValue valueWithCATransform3D:forward],
                            [NSValue valueWithCATransform3D:back],
                            [NSValue valueWithCATransform3D],
                            [NSValue valueWithCATransform3D:back2],
                            [NSValue valueWithCATransform3D:CATransform3DIdentity],
                            nil];
    [bounce setValues:bounceValues];
    
    //Set the duration
    [bounce setDuration:2.0];
    
    //Bronze Challenge - Change the opacity with the "bouncing"
    CAKeyframeAnimation *opacity = [CAKeyframeAnimation animationWithKeyPath:@"opacity"];
    NSArray *vals = [NSArray arrayWithObjects:[NSNumber numberWithFloat:forward.m11 - 1.0],
                    [NSNumber numberWithFloat:back.m11],
                    [NSNumber numberWithFloat:forward2.m11 - 1.0],
                    [NSNumber numberWithFloat:back2.m11],
                     nil];
    [opacity setValues:vals];
    [opacity setDuration: [bounce duration]];
    
    CAAnimationGroup *group = [CAAnimationGroup animation];
    [group setAnimations:[NSArray arrayWithObjects:bounce, opacity, nil]];
    [[timeLabel layer] addAnimation:group forKey:@"bounce,opacity"];
    
    //Animate the layer
//    [[timeLabel layer] addAnimation:bounce forKey:@"bounceAnimation"];
}

#2

Hmmmm… strange. I had to explicity set the duration of my group animation. It was getting cut short until I added:


#3

This is what I did. Slightly similar.

bbounceTimeLabel
[/b][code]
// Create a key frame animation
CAKeyframeAnimation *bounce = [CAKeyframeAnimation animationWithKeyPath:@“transform”];
//CAKeyframeAnimation *opacity = [CAKeyframeAnimation animationWithKeyPath:@“alpha”];
CABasicAnimation *opacity = [CABasicAnimation animationWithKeyPath:@“opacity”];
[opacity setDuration:1];
[opacity setFromValue:[NSNumber numberWithDouble:0]];
[opacity setToValue:[NSNumber numberWithDouble:0.5]];

// Create the values it will pass through
CATransform3D forward = CATransform3DMakeScale(1.3, 1.3, 1);
CATransform3D back = CATransform3DMakeScale(0.7, 0.7, 1);
CATransform3D foward2 = CATransform3DMakeScale(1.2, 1.2, 1);
CATransform3D back2 = CATransform3DMakeScale(0.9, 0.9, 1);
    
[bounce setValues:[NSArray arrayWithObjects:
                   [NSValue valueWithCATransform3D:CATransform3DIdentity],
                   [NSValue valueWithCATransform3D:forward],
                   [NSValue valueWithCATransform3D:back],
                   [NSValue valueWithCATransform3D:foward2],
                   [NSValue valueWithCATransform3D:back2],
                   [NSValue valueWithCATransform3D:CATransform3DIdentity],
                   nil]];
// Set the duration
[bounce setDuration:0.6];

// Animate the layer
[[timeLabel layer] addAnimation:bounce forKey:@"bounceAnimation"];

[[timeLabel layer] addAnimation:opacity forKey:@"opacity"];

CAAnimationGroup *group = [CAAnimationGroup animation];
[group setAnimations:[NSArray arrayWithObjects:bounce, opacity,nil]];

[/code]


#4

When I changed the duration of the challenge to something larger, I noticed the opacity needed tweaking. The way I got things to match was by matching the numer of elements in the arrays to be equal. As a result, the first portion of bronze

  • (void) bounceTimeLabel looks like:
- (void)bounceTimeLabel
{
    // Create a key frame animation
    CAKeyframeAnimation *bounce = [CAKeyframeAnimation animationWithKeyPath:@"transform"];
    
    // Create the values it will pass through
    CATransform3D forward = CATransform3DMakeScale(1.3, 1.3, 1);
    CATransform3D back = CATransform3DMakeScale(.7, .7, 1);
    CATransform3D forward2 = CATransform3DMakeScale(1.2, 1.2, 1);
    CATransform3D back2 = CATransform3DMakeScale(.9, .9, 1);
    [bounce setValues:[NSArray arrayWithObjects:
                       [NSValue valueWithCATransform3D:CATransform3DIdentity],
                       [NSValue valueWithCATransform3D:forward],
                       [NSValue valueWithCATransform3D:back],
                       [NSValue valueWithCATransform3D],
                       [NSValue valueWithCATransform3D:back2],
                       [NSValue valueWithCATransform3D:CATransform3DIdentity], nil]];
    // Set the duration
    [bounce setDuration:4];
    
    // Bronze
    CAKeyframeAnimation *fader = [CAKeyframeAnimation animationWithKeyPath:@"opacity"];
    NSMutableArray *vals = [NSMutableArray array];
    [vals addObject:[NSNumber numberWithFloat:1]];
    [vals addObject:[NSNumber numberWithFloat:1]];
    [vals addObject:[NSNumber numberWithFloat:.2]];
    [vals addObject:[NSNumber numberWithFloat:1]];
    [vals addObject:[NSNumber numberWithFloat:.4]];
    [vals addObject:[NSNumber numberWithFloat:1]];

    [fader setValues:vals];
    [fader setDuration:4];

// Animation comes next!

I’m not sure if this is new to iOS 6, but I found the following 2 options both yielded concurrent animations:

Option 1:

    // Animate the layer
    [[timeLabel layer] addAnimation:bounce forKey:@"bounceAnimation"];
    [[timeLabel layer] addAnimation:fader forKey:@"faderAnimation"];

or Option 2:

    // Animate the layer
    CAAnimationGroup *group = [CAAnimationGroup animation];
    [group setAnimations:[NSArray arrayWithObjects:bounce, fader, nil]];
    [group setDuration:4]; // You need to set this or the animation ends early (also not in documentation)
    [[timeLabel layer] addAnimation:group forKey:@"groupAnimation"];

I would have thought that Option 1 would yield sequential animations, but the end result was the same as Option 2.