为什么当我为我的CABasicAnimation设置一个较低的持续时间值时,它会跳转吗?

示例项目: http : //cl.ly/1W3V3b0D2001

我使用CABasicAnimation来创build一个像饼图一样的进度指示器。 类似于iOS 7的应用程序下载animation:

在这里输入图像说明

animation设置如下:

 - (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.innerPie.strokeStart = 0; self.innerPie.strokeEnd = 0; [self.layer addSublayer:ring]; [self.layer addSublayer:self.innerPie]; self.progress = 0.0; } 

通过设置视图的进度触发animation:

 - (void)setProgress:(CGFloat)progress animated:(BOOL)animated { self.progress = progress; if (animated) { CGFloat totalDurationForFullCircleAnimation = 0.25; CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; self.innerPie.strokeEnd = progress; pathAnimation.delegate = self; pathAnimation.fromValue = @([self.innerPie.presentationLayer strokeEnd]); pathAnimation.toValue = @(progress); pathAnimation.duration = totalDurationForFullCircleAnimation * ([pathAnimation.toValue floatValue] - [pathAnimation.fromValue floatValue]); [self.innerPie addAnimation:pathAnimation forKey:@"strokeEndAnimation"]; } else { [CATransaction setDisableActions:YES]; [CATransaction begin]; self.innerPie.strokeEnd = progress; [CATransaction commit]; } } 

但是,如果将进度设置为较小的值(如0.25 ,则animation会跳转。 它顺时针转动一点,跳回去,然后像往常一样继续前进。 如果持续时间或进度设置得更高则不会发生这种情况

我该如何阻止跳跃? 除非进度非常低,否则这段代码在每种情况下都能正常工作。 我究竟做错了什么?

啊! 早些时候我们应该看到这一点。 问题是你的if (animated)块的这一行:

 self.innerPie.strokeEnd = progress; 

由于innerPie是一个核心animation层,这会导致一个隐含的animation(大多数属性更改)。 这个animation是与你自己的animation战斗。 您可以通过在设置strokeEnd时禁用隐式animation来防止发生这种情况:

 [CATransaction begin]; [CATransaction setDisableActions:YES]; self.innerPie.strokeEnd = progress; [CATransaction commit]; 

(请注意setDisableActions:如何在开始/提交内。)

或者,您可以删除自己的animation,并使用自动animation,使用setAnimationDuration:东西setAnimationDuration:来改变它的长度。

原始意见:

我的猜测是你的drawRect:被调用,它会重置你的strokeEnd值。 这些图层可能不应该在drawRect:设置drawRect:无论如何。 尝试将该设置移动到init方法或didMoveToWindow:或类似的。

如果这没有效果,我会build议每次调用方法时添加日志语句来跟踪progress值和[self.innerPie.presentationLayer strokeEnd] ; 也许他们没有做你期望的事情。

你为什么使用drawRect:? 在你的class级中不应该有drawRect:方法。

如果您还使用self.layer,则不应使用drawRect。 使用一个或另一个,从来都没有。

将其改为如下所示:

 - (id)initWithFrame:(CGRect)frame { if (!(self = [super initWithFrame:frame]) return nil; 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.innerPie.strokeStart = 0; self.innerPie.strokeEnd = 0; [self.layer addSublayer:ring]; [self.layer addSublayer:self.innerPie]; self.progress = 0.0; return self; } 

在相同距离内行驶时,时间越短,速度越高。 当你设定的时间太短,速度会非常高,这就是为什么它看起来像跳跃。