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

时间:2014-03-20 01:29:30

标签: ios objective-c uiview core-animation cabasicanimation

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

我正在使用CABasicAnimation创建一个类似饼图的进度指示器。类似于iOS 7应用程序下载动画:

enter image description here

动画设置如下:

- (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;
}

通过设置视图的进度来触发动画:

- (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,则动画会跳转。顺时针方向前进一点,向后跳,然后像往常一样继续往前走。如果持续时间或进度设置得更高,那么就不会发生这种情况。

如何停止跳跃?除非进度非常低,否则此代码适用于所有情况。我做错了什么?

3 个答案:

答案 0 :(得分:5)

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

self.innerPie.strokeEnd = progress;

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

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

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

或者,您可以删除自己的动画,只需使用自动动画,使用setAnimationDuration:之类的内容来更改动画长度。

原始建议:

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

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

答案 1 :(得分:1)

你为什么使用drawRect:?你班上应该没有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;
}

答案 2 :(得分:0)

在相同距离内行驶时,持续时间越短,速度越高。当您将持续时间设置得太小时,速度会非常高,这就是为什么它看起来像跳跃。