使用 UIBezierPath 动画饼图进度

Animating Pie Progress with UIBezierPath

问题:

如果你看一下我当前的代码,你会发现如果 targetSeconds 高于 ~2-3 秒,它工作正常。

但是,如果targetSeconds是0.005秒,它就不会工作,因为它不可能在0.005秒内完成100个方法调用。因此,有人对我可以做些什么来改进它有什么建议吗?我宁愿不包含第三方 GitHub 存储库。

当前代码:

// Target seconds means the seconds that it'll take to become 100.0f.    
- (void)startAnimatingWithTargetSeconds:(NSTimeInterval)targetSeconds{
// Try to set timeInterval to 0.005f and you'll see that it won't finish in 0.005f
  [NSTimer scheduledTimerWithTimeInterval:targetSeconds / 100.0f target:self selector:@selector(animateWithTimer:) userInfo:nil repeats:YES];
}


- (void)animateWithTimer:(NSTimer *)timer {
  BOOL isFinished = self.currentProgress >= 100;

  if (isFinished) {
    // Invalidate timer
    if (timer.isValid) {
      [timer invalidate];
    }

    // Reset currentProgress
    self.currentProgress = 0.0f;
   }else{
     if (timer.isValid) {
        self.currentProgress += 1;
     }
  }
}

// Overriden setter
- (void)setCurrentProgress:(CGFloat)currentProgress {
  if (_currentProgress == currentProgress) {
    return;
  }

  dispatch_async(dispatch_get_main_queue(), ^{
    _currentProgress = currentProgress;
    [self setNeedsDisplay];
  });
}

然后在 drawRect 中,我有一个 UIBezierPath 基本上根据 self.currentProgress 画圆。

像这样:CGFloat endAngle = (self.currentProgress / 100.0f) * 2 * M_PI + startAngle;

问题:

是否有任何公式或任何东西可以帮助我解决我的问题?因为如果我将 self.currentProgress 设置为 self.currentProgress += 5; 而不是 1,它的动画会快得多,这正是我正在寻找的。

我更喜欢使用这样的东西,因为计时器(和 usleep)在小间隔内工作非常不准确:

-(void)startAnimatingWithTargetSeconds:(NSTimeInterval)targetSeconds
{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        float fps = 60;
        float currentState = 0;
        float frameStateChange = fps/targetSeconds;
        NSDate *nextFrameDate = [NSDate date];
        while (currentState < 1) {
            currentState += frameStateChange;
            self.currentProgress = roundf(currentState * 100.);
            nextFrameDate = [nextFrameDate dateByAddingTimeInterval:1./fps];
            while ([nextFrameDate timeIntervalSinceNow] > 0) {
                usleep((useconds_t)(100000/fps));
            }
        }
        self.currentProgress = 0;
    });
}

首先,你希望什么时候每 0.005 秒重绘一次?那是 200 FPS,远远超出您的需要。

不要重新发明轮子——利用 Core Animation! Core Animation 已经知道如何以适当的速率调用您的状态更改函数,以及如何在必要时重绘视图,假设您告诉它该做什么。该策略的要点如下:

  1. 向您的图层添加一个动态 属性,表示您的饼图切片的完整性。
  2. 告诉 Core Animation 这个 属性 可以通过覆盖 actionForKey:, or setting the animation into the actions dictionary (or even more options, detailed here) 来设置动画。
  3. 告诉 Core Animation 对此 属性 的更改需要使用 needsDisplayForKey:.
  4. 重绘图层
  5. 实施 display 方法,根据动态 属性 的 表示层的 值重绘饼图。

完成!现在,您可以将动态 属性 从任何值设置为任何其他值,就像不透明度、位置等一样。Core Animation 负责计时和回调,您将获得 60 FPS 的平滑流畅。

有关示例,请参阅以下资源,按有用性递减的顺序列出(在我看来):

祝你好运!