Question

I currently have a timer below which animates a circlular line - a countdown line. This animates according to the amount of seconds i pass to _percent.

I would like to have another circular wheel that depicts seconds. However i am struggling on how to have an animation lasting 1 second for every second left in _percent. e.g. if percent is 100, i want circle to animate a loop 100 times lasting 1 second. (as well as achieving the animation effect shown below.

Here is a drawing for clarity:) The first row of 3 shows what it is like currently, and the bottom row of 3 shows the effect i desire... enter image description here

Here is how i fire off the timer in my view controller class:

-(void)viewDidLoad
{
       CPCircleCountdownView *m_testView;
    m_testView.percent = 100;
}

- (IBAction)StartTimerButtonPressed:(id)sender
{
    // Kick off a timer to count it down
    m_timer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(decrementSpin) userInfo:nil repeats:YES];
}

- (void)decrementSpin
{
    // If we can decrement our percentage, do so, and redraw the view
    if (m_testView.percent > 0) {
        m_testView.percent = m_testView.percent - 1;
        [m_testView setNeedsDisplay];
    }
    else {
        [m_timer invalidate];
        m_timer = nil;
    }
}

and here is my CPCountdownView where i want to add the new circular animation lasting one second for every second left. Both animations in theory should finish at the same time.

#import "CPCircleCountdownView.h"

@interface CPCircleCountdownView ()
{
    CGFloat startAngle;
    CGFloat endAngle;
}

@end

@implementation CPCircleCountdownView

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code

        self.backgroundColor = [UIColor clearColor];

        //get start and end angles
        startAngle = M_PI * 1.5;
        endAngle = startAngle + (M_PI * 2);
    }
    return self;
}

- (void)drawRect:(CGRect)rect
{
    //Display arc percent
    //NSString *textContent = [NSString stringWithFormat:@"%d", self.percent];    //TODO: Pass total number of seconds int

    //-------------------------
    UIBezierPath* bezierPath = [UIBezierPath bezierPath];
    // Create our arc, with the correct angles
    [bezierPath addArcWithCenter:CGPointMake(rect.size.width / 2, rect.size.height / 2)
                          radius:130
                      startAngle:startAngle
                        endAngle:(endAngle - startAngle) * (_percent / _overallScore) + startAngle
                       clockwise:YES];
    // Set the display for the path, and stroke it
    bezierPath.lineWidth = 2;
    [[UIColor colorWithRed:122.0 / 255.0 green:197.0 / 255.0 blue:205.0  / 255.0 alpha:1.0] setStroke];
    [bezierPath stroke];

    //-------------------------  
}

Any help would be great I'm relatively new so if you could give me examples that would be great :) Thanks

Was it helpful?

Solution

probably a better approach would be to use 2 CAShapeLayers and animate a bezier path. Assume CPCircleCountdownView is a UIView Subclass

 @interface CPCircleCountdownView ()

  @property (nonatomic) CAShapeLayer* layer1;
  @property (nonatomic) CAShapeLayer* layer2;
  @property (nonatomic, assign) float startAngle;
  @property (nonatomic, assign) float endAngle;    
 @end   

 @implementation CPCircleCountdownView

 - (void)intiWithFrame:(CGRect) Frame
 {
   self = [super initWithFrame:frame];
   if (self) {
      _startAngle = M_PI * 1.5;
     _endAngle = _startAngle + (M_PI * 2);

    }


 }

-(void)layoutSubviews{

    self.layer1 = [CAShapeLayer layer];
    self.layer1.fillColor = [UIColor clearColor].CGColor;
    self.layer1.strokeColor = [UIColor redColor].CGColor;

    self.layer2 = [CAShapeLayer layer];
    self.layer2.fillColor = [UIColor clearColor].CGColor;
    self.layer2.strokeColor = [UIColor greenColor].CGColor;

    [self.layer1 setFrame:CGRectMake(50, 50, 200, 200)];
    [self.layer2 setFrame:CGRectMake(50, 50, 200, 200)];
    UIBezierPath *path1 = [UIBezierPath bezierPathWithArcCenter:CGPointMake(self.layer1.frame.size.width / 2, self.layer1.frame.size.height / 2)
                                                     radius:130
                                                 startAngle:self.startAngle
                                                   endAngle:self.endAngle
                                                  clockwise:YES];

    UIBezierPath *path2 = [UIBezierPath bezierPathWithArcCenter:CGPointMake(self.layer2.frame.size.width / 2, self.layer2.frame.size.height / 2)
                                                     radius:125
                                                 startAngle:self.endAngle
                                                   endAngle:self.startAngle
                                                  clockwise:NO];

    self.layer1.path = path1.CGPath;
    self.layer2.path = path2.CGPath;
    [self.view.layer addSublayer:self.layer1];
    [self.view.layer addSublayer:self.layer2];
    [self.layer1  setStrokeEnd:1.0f];
    [self.layer2 setStrokeEnd:0.0f];
   // i've placed this here for convenience in testing, really it should be in method called by your button Action
    [self decrementSpinForValue:100];

}

- (void)animationDidStop:(CAAnimation *)animation finished:(BOOL)finished {
    [self.layer1 removeAllAnimations];
    [self.layer2 removeAllAnimations];
}


-(void) decrementSpinForValue:(float) percent{

    CABasicAnimation *strokeEndAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
    strokeEndAnimation.delegate = self;
    strokeEndAnimation.duration = percent;
    [strokeEndAnimation setFillMode:kCAFillModeForwards];
    strokeEndAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
    strokeEndAnimation.removedOnCompletion = NO;
    strokeEndAnimation.fromValue = [NSNumber numberWithFloat:1.0f];
    strokeEndAnimation.toValue = [NSNumber numberWithFloat:0.0f];
    [self.layer1 addAnimation:strokeEndAnimation forKey:@"progressOuterStatus"];

    CABasicAnimation *strokeEndAnimation2 = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
    strokeEndAnimation2.duration = 1.0f;
    strokeEndAnimation2.repeatCount = percent;
    [strokeEndAnimation2 setFillMode:kCAFillModeForwards];
    strokeEndAnimation2.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
    strokeEndAnimation2.removedOnCompletion = NO;
    strokeEndAnimation2.fromValue = [NSNumber numberWithFloat:0.0f];
    strokeEndAnimation2.toValue = [NSNumber numberWithFloat:1.0f];
    [self.layer2 addAnimation:strokeEndAnimation2 forKey:@"progressInnerStatus"];


}

@end

I think that gives you an idea of how you can accomplish it.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top