如何在 Core Animation 中平滑更新动画的 toValue/end 点?

Posted

技术标签:

【中文标题】如何在 Core Animation 中平滑更新动画的 toValue/end 点?【英文标题】:How do I smoothly update the toValue/end point of an animation in Core Animation? 【发布时间】:2014-03-18 01:32:46 【问题描述】:

我试图模仿 Apple 在 ios 7 中下载应用程序时作为进度指示器的效果,有点像饼图:

我的代码运行良好。我可以给它一个值,它会更新到那个值。

- (void)drawRect:(CGRect)rect

    [super drawRect:rect];

    CGFloat radius = CGRectGetWidth(self.frame) / 2;
    CGFloat inset  = 1;
    CAShapeLayer *ring = [CAShapeLayer layer];
    ring.path = [UIBezierPath bezierPathWithRoundedRect:CGRectInset(self.bounds, inset, inset)cornerRadius:radius-inset].CGPath;

    ring.fillColor = [UIColor clearColor].CGColor;
    ring.strokeColor = [UIColor whiteColor].CGColor;
    ring.lineWidth = 2;

    self.innerPie = [CAShapeLayer layer];
    inset = radius/2; 
    self.innerPie.path = [UIBezierPath bezierPathWithRoundedRect:CGRectInset(self.bounds, inset, inset)
                                               cornerRadius:radius-inset].CGPath;
    self.innerPie.fillColor   = [UIColor clearColor].CGColor;
    self.innerPie.strokeColor = [UIColor whiteColor].CGColor;
    self.innerPie.lineWidth   = (radius-inset)*2;

    [self.layer addSublayer:ring];
    [self.layer addSublayer:self.innerPie];

    self.progress = 0.0;
    self.innerPie.hidden = YES;


- (void)setProgress:(CGFloat)progress animated:(BOOL)animated 
    if (animated) 
        self.innerPie.hidden = NO;
        self.innerPie.strokeEnd = progress;

        CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
        pathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
        pathAnimation.duration = 0.5;
        pathAnimation.fromValue = [NSNumber numberWithFloat:self.progress];
        pathAnimation.toValue = [NSNumber numberWithFloat:progress];

        [self.innerPie addAnimation:pathAnimation forKey:@"strokeEnd"];
    
    else 
        [CATransaction setDisableActions:YES];
        [CATransaction begin];
        self.innerPie.hidden = NO;
        self.innerPie.strokeEnd = progress;
        [CATransaction commit];
    

    self.progress = progress;

我的问题在于在动画中间更新进度。例如,如果我正在下载一个文件并且进度从 0.1 跳到 0.2(10% 到 20%),我想对其进行动画处理。假设我给它一个 0.5 秒的动画。如果在 0.5 秒动画中的 0.2 秒跳到 0.4 (40%) 怎么办?

使用我当前的代码,它会跳到第一个动画 (20%) 中应该结束的位置(应该有动画的时间),然后再次开始向 40% 移动。

我想要的是它几乎将toValue 更新到 0.4。至少这是我认为我想要的。从概念上讲,我希望它在不中断先前值的情况下继续向新值播放动画。平滑度,简单来说。

我将如何做到这一点?

我知道有图书馆可以做到这一点。出于学习目的,我想自己做。

【问题讨论】:

【参考方案1】:

你应该替换

pathAnimation.fromValue = [NSNumber numberWithFloat:self.progress];

与:

pathAnimation.fromValue = [NSNumber numberWithFloat:[self.innerPie.presentationLayer strokeEnd]];

最初,[self.innerPie.presentationLayer strokeEnd]] 将为 1,因此您需要将初始值设置为 0。将这两行添加到您创建“innerPie”的位置:

self.innerPie.strokeStart = 0;
self.innerPie.strokeEnd = 0;

我已经测试过了,它可以工作。

【讨论】:

这很好用。我现在唯一的问题是是否有可能以一致的速度填充圆圈进度。现在,如果我填满它,不管它需要多少 0.5 秒。这意味着小填充将缓慢移动,而大填充将快速移动。是否可以让它以恒定的速度填充? @DougSmith:是的,这很简单。请注意,持续时间 = 距离 / 速度。这里的“距离”是 fromValue 和 toValue 之间的差异。 “速度”是您定义的值(例如,每 1 秒 0.5 次)。然后你可以计算“持续时间”。在 pathAnimation.duration = [上面计算的持续时间] 中使用它; @DougSmith :如果您能将我的帖子标记为已接受的答案(如果它适合您),将不胜感激。

以上是关于如何在 Core Animation 中平滑更新动画的 toValue/end 点?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 CamShift 中平滑跟踪

如何在python中平滑曲线

如何在纯 JavaScript 中平滑滚动到元素

如何在 Matlab 中平滑阶梯状图

如何在opengl中平滑多个四边形的纹理

如何在 iOS 中平滑放大和缩小 UIButton