Dario
Dario

Reputation: 1315

Core Animation: Setting animated property correctly

I am currently trying to build a bar graph view using Core Animation and layers. To make it cooler I tried to let each beam pop up a bit, one after another. For convenience I flipped the coordinate system of the view vertically.

Here's the code:

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        self.transform = CGAffineTransformMakeScale(1, -1);
        self.values = @[@12.5f, @4.25f, @23.0f, @3.0f, @17.9f, @7.0f, @15.1f];
    }
    return self;
}

- (void)didMoveToSuperview
{
    self.backgroundColor = [UIColor whiteColor];
    self.beamContainer = [CALayer layer];
    CGRect frame = CGRectInset(self.bounds, 20, 20);
    self.beamContainer.frame = frame;
    self.beamContainer.backgroundColor = [UIColor colorWithWhite:0.98 alpha:1].CGColor;
    float maxValue = [[self.values valueForKeyPath:@"@max.floatValue"] floatValue];
    for (int i = 0; i < self.values.count; i++) {
        CALayer *beam = [CALayer layer];
        CGFloat beamHeight = ([self.values[i] floatValue] * frame.size.height) / maxValue;

        beam.backgroundColor = [UIColor colorWithRed:0.5 green:0.6 blue:1 alpha:1].CGColor;
        beam.anchorPoint = CGPointMake(0, 0);
        beam.position = CGPointMake(frame.size.width * ((float)i/(float)self.values.count), 0);
        CGRect endBounds = CGRectMake(0, 0, frame.size.width /(float)self.values.count, beamHeight);
        CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"bounds"];
        animation.fromValue = [NSValue valueWithCGRect:CGRectMake(0, 0, frame.size.width /(float)self.values.count, 5)];
        animation.toValue = [NSValue valueWithCGRect:endBounds];
        animation.duration = .5;
        animation.beginTime = CACurrentMediaTime() + ((float)i * 0.1);
        [beam addAnimation:animation forKey:@"beamAnimation"];

        [self.beamContainer addSublayer:beam];
        beam.bounds = endBounds;
    }
    [self.layer addSublayer:self.beamContainer];
}

This works like a charm, the only problem is that if I don't write beam.bounds = endBounds; the beam will snap back to it's old bounds after the animation is finished. But when I do, It's going to use endBounds even before the animation has started and during the delay for each animation.

How can I animate the beam going from bounds A to B and sticking to B at the end?

Upvotes: 1

Views: 597

Answers (1)

Michael Campsall
Michael Campsall

Reputation: 4335

You could try something like the following:

// Save the original value
CGFloat originalY = layer.position.y;

// Change the model value (this is not animated yet)
layer.position = CGPointMake(layer.position.x, 300.0);

CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position.y"];

// Now specify the fromValue for the animation because
// the current model value is already the correct toValue
animation.fromValue = @(originalY);
animation.duration = 1.0;

// Use the name of the animated property as key
// to override the implicit animation
[layer addAnimation:animation forKey:@"position"];

although it is not an exact match for the bar graph animations you are trying, you can likely get the pattern from the above example. You can read more about it here. It helped me with a similar issue I had the other day.

Upvotes: 0

Related Questions