-1

I'm trying to build a simple pie-chart style progress indicator. It animates well, but after the animation concludes, it snaps right back to the previous value before the animation.

My code is as follows:

- (void)drawRect:(CGRect)rect
{
    [super drawRect:rect];

    // Create a white ring that fills the entire frame and is 2 points wide.
    // Its frame is inset 1 point to fit for the 2 point stroke width
    CGFloat radius = CGRectGetWidth(self.frame) / 2;
    CGFloat inset  = 1;
    CAShapeLayer *ring = [CAShapeLayer layer];
    ring.path = [UIBezierPath bezierPathWithRoundedRect:CGRectInset(self.bounds, inset, inset)
                                           cornerRadius:radius-inset].CGPath;

    ring.fillColor   = [UIColor clearColor].CGColor;
    ring.strokeColor = [UIColor whiteColor].CGColor;
    ring.lineWidth   = 2;

    // Create a white pie-chart-like shape inside the white ring (above).
    // The outside of the shape should be inside the ring, therefore the
    // frame needs to be inset radius/2 (for its outside to be on
    // the outside of the ring) + 2 (to be 2 points in).
    self.innerPie = [CAShapeLayer layer];
    inset = radius/2; // The inset is updated here
    self.innerPie.path = [UIBezierPath bezierPathWithRoundedRect:CGRectInset(self.bounds, inset, inset)
                                               cornerRadius:radius-inset].CGPath;
    self.innerPie.fillColor   = [UIColor clearColor].CGColor;
    self.innerPie.strokeColor = [UIColor whiteColor].CGColor;
    self.innerPie.lineWidth   = (radius-inset)*2;

    [self.layer addSublayer:ring];
    [self.layer addSublayer:self.innerPie];

    self.progress = 0.0;
    self.innerPie.hidden = YES;
}

- (void)setProgress:(CGFloat)progress animated:(BOOL)animated {
    self.innerPie.hidden = NO;

    CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
    pathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
    pathAnimation.duration = 3.0;
    pathAnimation.fromValue = [NSNumber numberWithFloat:_progress];
    pathAnimation.toValue = [NSNumber numberWithFloat:progress];
    [self.innerPie addAnimation:pathAnimation forKey:@"strokeEndAnimation"];

    if (_progress != progress) {
        _progress = progress;
    }
}

I simply set it up in drawRect then have a method to set the progress with an animation. What am I doing wrong here?

Doug Smith
  • 29,668
  • 57
  • 204
  • 388

1 Answers1

3

That's how core Animation works. What you want to do is to set the property to it's final value just before adding the animation to the layer. Then the animation hides the fact that you changed the property, and when the animation is removed after it's finished, the property is at it's end value.

Duncan C
  • 128,072
  • 22
  • 173
  • 272
  • What property would I be setting in this case? – Doug Smith Mar 17 '14 at 01:10
  • In this case it would be the strokeEnd property - See this question - http://stackoverflow.com/questions/16326623/how-do-i-make-a-layer-maintain-the-final-value-of-a-cabasicanimation – Paulw11 Mar 17 '14 at 01:20
  • As @Paulw11 says, for your code above, it's the layer's strokeEnd property. In more general terms, it's whatever property you're modifying with the animationWithKeyPath: key path that you specify when you create the animation. – Duncan C Mar 17 '14 at 11:24