Search code examples
iosobjective-ciphonensdatenstimer

How to set progress bar With timer after view controller loaded


I m using timer on circular progress bar and taken NSDate property in interface (tested also taking in viewWillAppear) but when viewController loads it show starting from 27 seconds remain, while i have set its value to 30 seconds.

My scenario is, a user has a question and optional answers and he/she has some time suppose 30 seconds when he/she click start button than another screen opens after this screen loads time start should be from 30 seconds.

@interface TestViewController ()

 {
    NSTimeInterval totalCountdownInterval;
    NSDate* startDate;
 }
}

-(void)viewWillAppear:(BOOL)animated{

    [self startTimer];
    totalCountdownInterval = 30.0;
    startDate = [NSDate date];
}

-(void)startTimer {

      if (!_timer) {
          _timer = [NSTimer scheduledTimerWithTimeInterval:1.0f
                                                  target:self
                                                selector:@selector(timerFired:)
                                                userInfo:nil
                                               repeats:YES];
    }
}

-(void)stopTimer {

      if ([_timer isValid]) {
          [_timer invalidate];
      }
      _timer = nil;
}

-(void)timerFired:(NSTimer *)timer {

    NSTimeInterval elapsedTime = [[NSDate date] timeIntervalSinceDate:startDate];
    NSTimeInterval remainingTime = totalCountdownInterval - elapsedTime;

    CGFloat timeRemain = remainingTime;
    NSLog(@"%f", timeRemain);
    if (remainingTime < 0.0) {
        [_timer invalidate];
    }

    [_circleProgressBar setHintTextGenerationBlock:(^NSString *(CGFloat progress) {

        if (!(timeRemain < 0.9)) {
            progress = timeRemain - 1;
            return [NSString stringWithFormat:@"%.0f", progress];
        }else{
            progress = 0;
            return [NSString stringWithFormat:@"%.0f", progress];
        }

    })];
    [_circleProgressBar setProgress:(_circleProgressBar.progress + 0.0333f) animated:YES];
}

-(void) viewWillDisappear:(BOOL)animated {
    [self stopTimer];
}

Any help will be appreciated. Thanks.


Solution

  • The first timer event will be fired after 1 second is passed, so you should see 29 first. So you should display the number 30 manually somehow, outside the timer event.

    And you subtract 1 in your code in setHintTextGenerationBlock : progress = timeRemain - 1; which will make you see 28 as initial.

    And as final you should start timer in viewDidAppear instead of viewWillAppear where you lose another second and come to 27.

    To correct code should be like:

    @interface TestViewController ()
    
     {
        NSTimeInterval totalCountdownInterval;
        NSDate* startDate;
     }
    }
    
    -(void)viewDidLoad {
        [_circleProgressBar setHintTextGenerationBlock:(^NSString *(CGFloat progress) {
    
            return [NSString stringWithFormat:@"%.0f", progress * 30];
    
        })];
        [_circleProgressBar setProgress:1.0) animated:NO];
    }
    
    -(void)viewDidAppear:(BOOL)animated{
    
        [self startTimer];
        totalCountdownInterval = 30.0;
        startDate = [NSDate date];
    }
    
    -(void)startTimer {
    
          if (!_timer) {
              _timer = [NSTimer scheduledTimerWithTimeInterval:1.0f
                                                      target:self
                                                    selector:@selector(timerFired:)
                                                    userInfo:nil
                                                   repeats:YES];
        }
    }
    
    -(void)stopTimer {
    
          if ([_timer isValid]) {
              [_timer invalidate];
          }
          _timer = nil;
    }
    
    -(void)timerFired:(NSTimer *)timer {
    
        NSTimeInterval elapsedTime = [[NSDate date] timeIntervalSinceDate:startDate];
        NSTimeInterval remainingTime = totalCountdownInterval - elapsedTime;
    
        CGFloat timeRemain = remainingTime;
        NSLog(@"%f", timeRemain);
        if (remainingTime < 0.0) {
            [_timer invalidate];
        }
    
        [_circleProgressBar setProgress:(1.0 * timeRemain / 30.0) animated:YES];
    }
    
    -(void) viewWillDisappear:(BOOL)animated {
        [self stopTimer];
    }
    

    You should set progress to 1.0 in the beginning and count down to 0.0 in 30 steps. Set hint generation block to display something meaningful for 1.0 - 0.0 as 30 - 0.