如何改变CALayer动画的方向

时间:2015-03-14 21:58:12

标签: ios objective-c iphone animation core

如何更改CALayer动画的方向和起始位置?

例如,如果我使用以下代码为圆形设置动画:

-(void)drawCircle {
    CAShapeLayer *circleShapeLayer = [[CAShapeLayer alloc] init];
    circleShapeLayer.path          = [self circleBezierPath].CGPath;
    circleShapeLayer.strokeColor   = [UIColor lightGrayColor].CGColor;
    circleShapeLayer.fillColor     = [UIColor clearColor].CGColor;
    circleShapeLayer.lineWidth     = 2;
    [self.view.layer addSublayer:circleShapeLayer];

    CABasicAnimation *circleShapeAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
    circleShapeAnimation.timingFunction    = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    circleShapeAnimation.duration          = 1.0f;
    circleShapeAnimation.fromValue         = @(0.0f);
    circleShapeAnimation.toValue           = @(1.0f);
    [circleShapeLayer addAnimation:circleShapeAnimation forKey:@"strokeEnd"];
}

-(UIBezierPath *)circleBezierPath {
    UIBezierPath *circlePath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(100, 100, 100, 100)];
    return circlePath;
}

动画始终从3点开始,并以顺时针方向动画。

如何强制它从12点(或任何其他位置)开始并以逆时针方向动画?

1 个答案:

答案 0 :(得分:9)

发生了什么

当您在矩形中创建一个椭圆(使用CGPath函数的UIBezierPath时,它),它会创建一个适合指定矩形的椭圆或椭圆。这意味着椭圆的中心与矩形的原点不同:

An oval in a rectangle

动画始终从3点开始逆时针旋转,因为这是在默认坐标系中从0到2π的圆弧:

Angles in the default coordinate system

这意味着你动画笔画的路径看起来真的如下:

enter image description here

解决问题的错误方法

查看路径,您可以通过旋转视图来移动起点。不建议这样做,因为它有副作用。如果有任何子图层,那么它们也会被旋转。可以通过翻转视图(沿一个轴缩放阴影)或通过将笔划的开始从1设置为0而不是笔划结束来更改笔划方向。

所有这些解决方案都有效,但有副作用,并且不太清楚什么是优选的。

更好的解决方案

我喜欢看这样的问题:动画代码正在做正确的事情,动画从无到有动画到所有动画,但路径的起点和笔画方向不是预期的。这意味着“问题”在于路径。

您可以使用从任何起始角度到顺时针或逆时针方向的任何结束角度的弧线来创建自己的整圆,而不是使用bezierPathWithOvalInRect:。所需要的只是你计算圆的中心(以及起点和终点)。查看默认坐标系,我们可以看到起始角度为3π/ 2或-π/ 2。然后,结束角度从起始角度加上或减去2π(取决于它是顺时针还是逆时针)。

创建这样的弧可能如下所示:

- (UIBezierPath *)circleBezierPath
{
    CGRect boundingRect = CGRectMake(100, 100, 100, 100);
    CGPoint center      = CGPointMake(CGRectGetMidX(boundingRect), CGRectGetMidY(boundingRect));
    CGFloat radius      = MIN(CGRectGetWidth(boundingRect), CGRectGetHeight(boundingRect));

    CGFloat twelveOClockAngle = -M_PI_2;
    BOOL clockwise = NO; // NO for counter clockwise

    CGFloat startAngle = twelveOClockAngle;
    CGFloat endAngle = startAngle + (2.0*M_PI * (clockwise ? 1.0 : -1.0));

    return [UIBezierPath bezierPathWithArcCenter:center
                                          radius:radius
                                      startAngle:startAngle
                                        endAngle:endAngle
                                       clockwise:clockwise];
}