CoreGraphics 圆形动画持续时间错误
CoreGraphics Circle Animation Wrong Duration
编辑:我认为最好的办法是将代码转换为CoreAnimation。该代码将如何转换为该方法?我认为问题在于设备 FPS 存在差异。
我一直在寻找 iOS 的自定义进度圈并遇到这个库:https://github.com/Eclair/CircleProgressBar
但是我注意到每当动画超过 5 秒时就会出现错误。圆圈的动画比应有的慢。例如:我将圆的动画设置为在 30 秒内完成 100%,但它在那个时间段内仅达到 78%("Done" 标签在 NSTimer 30 秒后可见)。查看我在制作的演示项目中拍摄的这张图片:
这是制作圆圈的代码:
- (void)drawProgressBar:(CGContextRef)context progressAngle:(CGFloat)progressAngle center:(CGPoint)center radius:(CGFloat)radius {
CGFloat barWidth = self.progressBarWidthForDrawing;
if (barWidth > radius) {
barWidth = radius;
}
CGContextSetFillColorWithColor(context, self.progressBarProgressColorForDrawing.CGColor);
CGContextBeginPath(context);
CGContextAddArc(context, center.x, center.y, radius, DEGREES_TO_RADIANS(_startAngle), DEGREES_TO_RADIANS(progressAngle), 0);
CGContextAddArc(context, center.x, center.y, radius - barWidth, DEGREES_TO_RADIANS(progressAngle), DEGREES_TO_RADIANS(_startAngle), 1);
CGContextClosePath(context);
CGContextFillPath(context);
CGContextSetFillColorWithColor(context, self.progressBarTrackColorForDrawing.CGColor);
CGContextBeginPath(context);
CGContextAddArc(context, center.x, center.y, radius, DEGREES_TO_RADIANS(progressAngle), DEGREES_TO_RADIANS(_startAngle + 360), 0);
CGContextAddArc(context, center.x, center.y, radius - barWidth, DEGREES_TO_RADIANS(_startAngle + 360), DEGREES_TO_RADIANS(progressAngle), 1);
CGContextClosePath(context);
CGContextFillPath(context);
}
这是使圆动画化的代码:
- (void)animateProgressBarChangeFrom:(CGFloat)startProgress to:(CGFloat)endProgress duration:(CGFloat)duration {
_currentAnimationProgress = _startProgress = startProgress;
_endProgress = endProgress;
_animationProgressStep = (_endProgress - _startProgress) * AnimationChangeTimeStep / duration;
_animationTimer = [NSTimer scheduledTimerWithTimeInterval:AnimationChangeTimeStep target:self selector:@selector(updateProgressBarForAnimation) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:_animationTimer forMode:NSRunLoopCommonModes];
}
- (void)updateProgressBarForAnimation {
_currentAnimationProgress += _animationProgressStep;
_progress = _currentAnimationProgress;
if ((_animationProgressStep > 0 && _currentAnimationProgress >= _endProgress) || (_animationProgressStep < 0 && _currentAnimationProgress <= _endProgress)) {
[_animationTimer invalidate];
_animationTimer = nil;
_progress = _endProgress;
}
[self setNeedsDisplay];
}
这里有什么地方看起来不对吗?有没有更好的方法来制作圆圈动画?理想情况下,我希望它以正确的持续时间进行动画处理,但我只是不太熟悉如何解决这个问题。
展示问题的演示项目:
https://ufile.io/8pkt5
基本上 drawRect 方法适用于主线程,因为您使用的 AnimationChangeTimeStep 太小:
const CGFloat AnimationChangeTimeStep = 0.01f;
它是每秒 100 帧,这太多了并且 drawRect 阻塞了主线程和附加到该线程的动画计时器,因此动画计时器的重复不会立即触发,而是在所有当前 drawRect 调用完成后触发。
因此,要解决您的问题,只需减少 AnimationChangeTimeStep 并使其等于帧速率 30 fps:
const CGFloat AnimationChangeTimeStep = 1/30.0f;
编辑:我认为最好的办法是将代码转换为CoreAnimation。该代码将如何转换为该方法?我认为问题在于设备 FPS 存在差异。
我一直在寻找 iOS 的自定义进度圈并遇到这个库:https://github.com/Eclair/CircleProgressBar
但是我注意到每当动画超过 5 秒时就会出现错误。圆圈的动画比应有的慢。例如:我将圆的动画设置为在 30 秒内完成 100%,但它在那个时间段内仅达到 78%("Done" 标签在 NSTimer 30 秒后可见)。查看我在制作的演示项目中拍摄的这张图片:
这是制作圆圈的代码:
- (void)drawProgressBar:(CGContextRef)context progressAngle:(CGFloat)progressAngle center:(CGPoint)center radius:(CGFloat)radius {
CGFloat barWidth = self.progressBarWidthForDrawing;
if (barWidth > radius) {
barWidth = radius;
}
CGContextSetFillColorWithColor(context, self.progressBarProgressColorForDrawing.CGColor);
CGContextBeginPath(context);
CGContextAddArc(context, center.x, center.y, radius, DEGREES_TO_RADIANS(_startAngle), DEGREES_TO_RADIANS(progressAngle), 0);
CGContextAddArc(context, center.x, center.y, radius - barWidth, DEGREES_TO_RADIANS(progressAngle), DEGREES_TO_RADIANS(_startAngle), 1);
CGContextClosePath(context);
CGContextFillPath(context);
CGContextSetFillColorWithColor(context, self.progressBarTrackColorForDrawing.CGColor);
CGContextBeginPath(context);
CGContextAddArc(context, center.x, center.y, radius, DEGREES_TO_RADIANS(progressAngle), DEGREES_TO_RADIANS(_startAngle + 360), 0);
CGContextAddArc(context, center.x, center.y, radius - barWidth, DEGREES_TO_RADIANS(_startAngle + 360), DEGREES_TO_RADIANS(progressAngle), 1);
CGContextClosePath(context);
CGContextFillPath(context);
}
这是使圆动画化的代码:
- (void)animateProgressBarChangeFrom:(CGFloat)startProgress to:(CGFloat)endProgress duration:(CGFloat)duration {
_currentAnimationProgress = _startProgress = startProgress;
_endProgress = endProgress;
_animationProgressStep = (_endProgress - _startProgress) * AnimationChangeTimeStep / duration;
_animationTimer = [NSTimer scheduledTimerWithTimeInterval:AnimationChangeTimeStep target:self selector:@selector(updateProgressBarForAnimation) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:_animationTimer forMode:NSRunLoopCommonModes];
}
- (void)updateProgressBarForAnimation {
_currentAnimationProgress += _animationProgressStep;
_progress = _currentAnimationProgress;
if ((_animationProgressStep > 0 && _currentAnimationProgress >= _endProgress) || (_animationProgressStep < 0 && _currentAnimationProgress <= _endProgress)) {
[_animationTimer invalidate];
_animationTimer = nil;
_progress = _endProgress;
}
[self setNeedsDisplay];
}
这里有什么地方看起来不对吗?有没有更好的方法来制作圆圈动画?理想情况下,我希望它以正确的持续时间进行动画处理,但我只是不太熟悉如何解决这个问题。
展示问题的演示项目: https://ufile.io/8pkt5
基本上 drawRect 方法适用于主线程,因为您使用的 AnimationChangeTimeStep 太小:
const CGFloat AnimationChangeTimeStep = 0.01f;
它是每秒 100 帧,这太多了并且 drawRect 阻塞了主线程和附加到该线程的动画计时器,因此动画计时器的重复不会立即触发,而是在所有当前 drawRect 调用完成后触发。 因此,要解决您的问题,只需减少 AnimationChangeTimeStep 并使其等于帧速率 30 fps:
const CGFloat AnimationChangeTimeStep = 1/30.0f;