将 UIProgressView 设为倒数计时器
Make UIProgressView as a count down timer
我实现了一个 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 毫秒。设备屏幕更新率为60frames/sec,即16.7msec/frame。所以总的滑点小于一帧时间,用户不会注意到。
正如 rmaddy 在评论中指出的那样,使用 NSTimer
可以避免大部分滑点。但是,最后一个计时器事件仍可能会延迟任意时间。
要使进度动画化,请在您的 decreaseProgress 方法中尝试此代码
[UIView animateWithDuration:1.5 animations:^{
[_timeLineProgress setProgress:0.0 animated:YES];
}];
我实现了一个 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 毫秒。设备屏幕更新率为60frames/sec,即16.7msec/frame。所以总的滑点小于一帧时间,用户不会注意到。
正如 rmaddy 在评论中指出的那样,使用 NSTimer
可以避免大部分滑点。但是,最后一个计时器事件仍可能会延迟任意时间。
要使进度动画化,请在您的 decreaseProgress 方法中尝试此代码
[UIView animateWithDuration:1.5 animations:^{
[_timeLineProgress setProgress:0.0 animated:YES];
}];