0

I've implemented a UIProgressView as a count down timer, its value is decreased from 1.0 to 0.0 in 1.5 seconds.

It already worked but the problem is the time it took was longer than 1.5 seconds, it was about 2.0 - 2.5 seconds until the progress view value reach 0.0

For it to run in just 1.5, I decreased the value of the progress by 0.001 each time it's called. The decrease method is call after 0.0015 second interval.

Below is how I do it. I don't know if there's anything wrong that make it run longer than just 1.5 seconds?

- (void)decreaseProgress {
    if (currentProgress > 0.0000f) {
        currentProgress -= 0.001f;
        [_timeLineProgress setProgress:currentProgress animated:YES];

        [self performSelector:@selector(decreaseProgress) withObject:self afterDelay:0.0015 inModes:@[NSDefaultRunLoopMode]];
    } else {
        [self stopTimer];
    }
}
JozackOverFlow
  • 267
  • 4
  • 19

2 Answers2

3

To animate the progress, try this code in your decreaseProgress method

[UIView animateWithDuration:1.5 animations:^{
        [_timeLineProgress setProgress:0.0 animated:YES];
    }];
Vinay Jain
  • 1,653
  • 20
  • 28
  • Thanks for your solution Vinay. I think this would work, but I have a few other action while the progress is running, this way it could animate smoothly but I won't control it overall, something like reset the progress while it's running. – JozackOverFlow Jul 01 '15 at 07:55
2

You're attempting to update the progress view 1000 times in 1.5 seconds. That's way too fast, since the screen only updates 60 times per second. In other words, you're updating the progress bar more than 10 times between each time that the progress bar is actually redrawn on the screen.

Instead I would suggest 15 updates at 0.1 second intervals, and change the progress bar by 1/15 each time.

One way to check how well the code is performing is to use the CACurrentMediaTime function to get timestamps. Here's some sample code that demonstrates how to do that. The progressStart variable is the timestamp when the button press event occurred, and the NSLog prints the amount of time elapsed relative to the start time.

An important feature of the code is that the performSelector method is called as early as possible in the updateProgress method, to minimize slippage.

@interface ViewController ()
{
    CFTimeInterval progressStart;
    int progressCount;
}
@property (weak, nonatomic) IBOutlet UIProgressView *progressView;
@end

- (void)updateProgress
{
    if ( progressCount > 0 )
        [self performSelector:@selector(updateProgress) withObject:nil afterDelay:0.1];

    self.progressView.progress = progressCount / 15.0;
    NSLog( @"%2d %.3lf", progressCount, CACurrentMediaTime() - progressStart );
    progressCount--;
}

- (IBAction)someButtonPressed
{
    self.progressView.progress = 1.0;
    progressStart = CACurrentMediaTime();
    progressCount = 15;
    [self updateProgress];
}

And here are the results from a typical run

2015-07-01 13:05:57.610 Progress[8354:907] 15 0.000
2015-07-01 13:05:57.711 Progress[8354:907] 14 0.101
2015-07-01 13:05:57.813 Progress[8354:907] 13 0.203
2015-07-01 13:05:57.914 Progress[8354:907] 12 0.304
2015-07-01 13:05:58.015 Progress[8354:907] 11 0.405
2015-07-01 13:05:58.116 Progress[8354:907] 10 0.506
2015-07-01 13:05:58.218 Progress[8354:907]  9 0.608
2015-07-01 13:05:58.319 Progress[8354:907]  8 0.709
2015-07-01 13:05:58.420 Progress[8354:907]  7 0.810
2015-07-01 13:05:58.520 Progress[8354:907]  6 0.910
2015-07-01 13:05:58.621 Progress[8354:907]  5 1.011
2015-07-01 13:05:58.722 Progress[8354:907]  4 1.112
2015-07-01 13:05:58.823 Progress[8354:907]  3 1.213
2015-07-01 13:05:58.924 Progress[8354:907]  2 1.314
2015-07-01 13:05:59.024 Progress[8354:907]  1 1.415
2015-07-01 13:05:59.125 Progress[8354:907]  0 1.515

Note that the performSelector:afterDelay method has about 1 millisecond of slippage on each event. The total slippage was 15 milliseconds. The device screen update rate is 60 frames/sec, which is 16.7 msec/frame. So the total slippage was less than one frame time, and won't be noticeable to the user.

As rmaddy pointed out in the comments, using an NSTimer allows you to avoid most of the slippage. However, the last timer event could still slip by an arbitrary amount of time.

user3386109
  • 34,287
  • 7
  • 49
  • 68
  • Thanks for your reply, I just tried you suggestion but the progress bar still run in 2 seconds. I don't know why? – JozackOverFlow Jul 01 '15 at 04:42
  • 1
    It depends on what else the system is doing. The `performSelector:afterDelay` method only specifies a minimum delay. The actual delay could be longer if the system is busy. – user3386109 Jul 01 '15 at 04:48
  • So there isn't any way to make it run exactly 1.5 seconds? – JozackOverFlow Jul 01 '15 at 04:49
  • Thanks so much for your detail code and log. I've implemented as you said and it still ran a bit longer than 1.5 seconds but that was acceptable in my case. – JozackOverFlow Jul 02 '15 at 03:57