Ios 将UIProgressView设置为倒计时计时器

Ios 将UIProgressView设置为倒计时计时器,ios,objective-c,uiprogressview,uiprogressbar,Ios,Objective C,Uiprogressview,Uiprogressbar,我实现了一个UIProgressView作为倒计时计时器,它的值在1.5秒内从1.0降低到0.0 它已经起作用了,但问题是它花费的时间超过了1.5秒 秒,大约2.0-2.5秒,直到进度视图值达到0.0 为了使它只在1.5中运行,每次调用它时,我都会将进度值减少0.001。减少方法在0.0015秒间隔后调用 下面是我怎么做的。我不知道是不是出了什么问题,导致它的运行时间超过了1.5秒 - (void)decreaseProgress { if (currentProgress > 0

我实现了一个UIProgressView作为倒计时计时器,它的值在1.5秒内从1.0降低到0.0

它已经起作用了,但问题是它花费的时间超过了1.5秒 秒,大约2.0-2.5秒,直到进度视图值达到0.0

为了使它只在1.5中运行,每次调用它时,我都会将进度值减少0.001。减少方法在0.0015秒间隔后调用

下面是我怎么做的。我不知道是不是出了什么问题,导致它的运行时间超过了1.5秒

- (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];
    }
}

您正试图在1.5秒内更新进度视图1000次。这太快了,因为屏幕每秒只更新60次。换句话说,在每次屏幕上实际重画进度条之间,更新进度条的次数超过10次

相反,我建议每隔0.1秒更新15次,每次将进度条更改1/15

检查代码执行情况的一种方法是使用
CACurrentMediaTime
函数获取时间戳。下面是一些示例代码,演示了如何做到这一点。
progressStart
变量是按钮按下事件发生时的时间戳,
NSLog
打印相对于开始时间经过的时间量

该代码的一个重要特性是在
updateProgress
方法中尽早调用
performSelector
方法,以最大限度地减少滑动

@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];
}
下面是一次典型运行的结果

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
请注意,
performSelector:afterDelay
方法在每个事件上有大约1毫秒的滑动。总滑动时间为15毫秒。设备屏幕更新速率为60帧/秒,即16.7毫秒/帧。因此,总的滑动时间小于一帧时间,用户不会注意到


正如rmaddy在评论中指出的那样,使用
NSTimer
可以避免大部分的滑动。但是,最后一个计时器事件仍可能以任意时间量滑动

您正试图在1.5秒内更新进度视图1000次。这太快了,因为屏幕每秒只更新60次。换句话说,在每次屏幕上实际重画进度条之间,更新进度条的次数超过10次

相反,我建议每隔0.1秒更新15次,每次将进度条更改1/15

检查代码执行情况的一种方法是使用
CACurrentMediaTime
函数获取时间戳。下面是一些示例代码,演示了如何做到这一点。
progressStart
变量是按钮按下事件发生时的时间戳,
NSLog
打印相对于开始时间经过的时间量

该代码的一个重要特性是在
updateProgress
方法中尽早调用
performSelector
方法,以最大限度地减少滑动

@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];
}
下面是一次典型运行的结果

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
请注意,
performSelector:afterDelay
方法在每个事件上有大约1毫秒的滑动。总滑动时间为15毫秒。设备屏幕更新速率为60帧/秒,即16.7毫秒/帧。因此,总的滑动时间小于一帧时间,用户不会注意到


正如rmaddy在评论中指出的那样,使用
NSTimer
可以避免大部分的滑动。但是,最后一个计时器事件仍可能以任意时间量滑动

若要设置进度动画,请在decreaseProgress方法中尝试此代码

[UIView animateWithDuration:1.5 animations:^{
        [_timeLineProgress setProgress:0.0 animated:YES];
    }];

要设置进度动画,请在decreaseProgress方法中尝试此代码

[UIView animateWithDuration:1.5 animations:^{
        [_timeLineProgress setProgress:0.0 animated:YES];
    }];


您应该改为使用NSTimer,并根据实际运行时间计算进度。@rmaddy它会有很大区别吗?因为我确实使用了NSTimer,但仍然遇到同样的问题。这就是为什么进度需要基于实际经过的时间,而不是一些递增的值。@rmaddy您有任何示例代码或链接吗?这可能会有帮助。您可以将其作为答案发布,如果它确实有助于我解决此问题,我将接受它。难道您不能仅通过在1.5秒内设置从1.0到0.0的进度动画来实现吗?您应该使用NSTimer,并根据实际经过的时间来确定进度。@rmaddy这会有很大的区别吗?因为我确实使用了NSTimer,但仍然遇到同样的问题。这就是为什么进度需要基于实际经过的时间,而不是一些递增的值。@rmaddy您有任何示例代码或链接吗?这可能会有帮助。你可以将其作为答案发布,如果它确实有助于我解决此问题,我将接受它。难道你不能在1.5秒内将进度从1.0设置为0.0吗?谢谢你的回复,我刚刚尝试了你的建议,但进度条仍在2秒内运行。我不知道为什么?这取决于系统在做什么。
performSelector:afterDelay
方法仅指定最小延迟。如果系统繁忙,实际延迟可能会更长。因此,没有任何方法可以使其精确运行1.5秒?非常感谢您提供的详细代码和日志。我已经按照你说的实现了,但仍然运行了1.5秒多一点,但这在我的情况下是可以接受的。谢谢你的回复,我刚刚尝试了你的建议,但进度条仍然在2秒内运行。我不知道为什么?这取决于系统在做什么。
performSelector:afterDelay
方法仅指定最小延迟。如果系统繁忙,实际延迟可能会更长。因此,没有任何方法可以使其精确运行1.5秒?非常感谢您提供的详细代码和日志。我已经按照你说的实现了,它仍然运行了1.5秒多一点,但这在我的情况下是可以接受的。谢谢你的解决方案Vinay。我想这会管用的,但在进展过程中,我还有一些其他的动作,