动画时更改CALayer颜色

yun*_*nas 1 objective-c calayer cabasicanimation cashapelayer catransition

方法1

CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"strokeStart"];
 [CATransaction begin];
    {
        [CATransaction setAnimationDuration:15];//Dynamic Duration
        [CATransaction setCompletionBlock:^{

        }];

        animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
        animation.autoreverses = NO;
        animation.removedOnCompletion = NO;
        animation.fromValue = @0;
        animation.toValue = @1;
        animation.timeOffset = 0;
        animation.fillMode = kCAFillModeForwards;
        [self.pathLayer addAnimation:animation forKey:animationKey];

    }
    [CATransaction commit];
Run Code Online (Sandbox Code Playgroud)

我在我的视图中添加了CAShapeLayer(pathLayer),我想让它在具有笔触效果的视图周围动画,上面的代码可以完成工作,但我的问题是以3个相等的比例更改颜色.所以我假设是重复上面的代码3次,并按相应的顺序更改以下行.

为第1

    animation.fromValue = @0;
    animation.toValue = @(1/3);
    animation.timeOffset = 0;
Run Code Online (Sandbox Code Playgroud)

第二名

    animation.fromValue = @(1/3);
    animation.toValue = @(2/3);
    animation.timeOffset = 0;// I don't know how to exactly set this  active local 
time since the duration which is currently 15 is dynamic can be 30 or 10.
Run Code Online (Sandbox Code Playgroud)

为第3

    animation.fromValue = @(2/3);
    animation.toValue = @(3);
    animation.timeOffset = 0;// Active local time- Not sure how and which value to set 
Run Code Online (Sandbox Code Playgroud)

方法2

使用偏移技术代替3个事务,让第1个完成时启动第2个事务,第2个时启动第3个 但是,当完成一个延迟/加速时,开始新动画所花费的时间的一小部分是可见的.

方法3

SubClass CAShapeLayer

通过执行SubClass,drawInContext方法只被调用一次,并且如果添加了一些额外的属性并且它被更改,则重复调用drawInContext方法,这样可以在特定的进度时间段之后更改图层颜色.但是覆盖drawInContext方法不能达到目的.

有什么建议 ?我不想单独实施NSTimer.

ipm*_*mcc 8

我不是100%清楚你想要什么,但如果目标只是为了整个笔画改变颜色,但在三个不连续的阶段,那么我建议添加以下.我在默认的"单视图应用程序"模板中编写了这些示例.我有一个按钮设置,其动作指向-doStuff:.如果整个笔画颜色要改变,它可能看起来像这样:

整个笔画改变颜色

为了生成它,代码看起来像:

@implementation MyViewController
{
    CAShapeLayer* mLayer;
}

- (IBAction)doStuff:(id)sender
{
    const NSUInteger numSegments = 3;
    const CFTimeInterval duration = 2;

    [mLayer removeFromSuperlayer];

    mLayer = [[CAShapeLayer alloc] init];
    mLayer.frame = CGRectInset(self.view.bounds, 100, 200);
    mLayer.fillColor = [[UIColor purpleColor] CGColor];
    mLayer.lineWidth = 12.0;
    mLayer.lineCap = kCALineCapSquare;
    mLayer.strokeEnd = 0.0;
    mLayer.path = [[UIBezierPath bezierPathWithRect: mLayer.bounds] CGPath]; // This can be whatever.

    [self.view.layer addSublayer: mLayer];

    [CATransaction begin];
    {
        [CATransaction setAnimationDuration: duration];// Dynamic Duration
        [CATransaction setCompletionBlock:^{ NSLog(@"Done"); }];

        const double portion = 1.0 / ((double)numSegments);
        NSMutableArray* values = [NSMutableArray arrayWithCapacity: numSegments];
        NSMutableArray* times = [NSMutableArray arrayWithCapacity: numSegments + 1];
        for (NSUInteger i = 0; i < numSegments; i++)
        {
            [values addObject: (__bridge id)[[UIColor colorWithHue: i * portion saturation:1 brightness:1 alpha:1] CGColor]];
            [times addObject: @(i * portion)];
        }

        [times addObject: @(1.0)]; // Have to add this, otherwise the last value wont get used.

        {
            CAKeyframeAnimation* animation = [CAKeyframeAnimation animationWithKeyPath: @"strokeColor"];
            animation.keyTimes = times;
            animation.values = values;
            animation.calculationMode = kCAAnimationDiscrete;
            animation.removedOnCompletion = NO;
            animation.timeOffset = 0;
            animation.fillMode = kCAFillModeForwards;
            [mLayer addAnimation: animation forKey: @"strokeColor"];
        }
        {
            CABasicAnimation* animation = [CABasicAnimation animationWithKeyPath: @"strokeEnd"];
            animation.fromValue = @(0);
            animation.toValue = @(1);
            animation.removedOnCompletion = NO;
            animation.timeOffset = 0;
            animation.fillMode = kCAFillModeForwards;
            [mLayer addAnimation: animation forKey: @"strokeEnd"];
        }
    }
    [CATransaction commit];
}

@end
Run Code Online (Sandbox Code Playgroud)

或者,如果目标是使用三个不同的笔划段,所有段都有不同的颜色,那就更复杂了,但仍然可以使用相同的基本原理.需要注意的一点是,如果没有自定义绘图,您的CAShapeLayers不能有多种笔触颜色(AFAIK),因此您需要将其分解为多个子图层.

下一个示例将形状图层放入视图中,然后为笔划的每个部分添加子图层,并设置动画,使其显示为绘制单个多色笔画,其中每个段是单独的颜色.这大致是它的样子:

输出的动画GIF

这是代码:

@implementation MyViewController
{
    CAShapeLayer* mLayer;
}

- (IBAction)doStuff:(id)sender
{
    const NSUInteger numSegments = 3;
    const CFTimeInterval duration = 2;

    [mLayer removeFromSuperlayer];

    mLayer = [[CAShapeLayer alloc] init];
    mLayer.frame = CGRectInset(self.view.bounds, 100, 200);
    mLayer.fillColor = [[UIColor purpleColor] CGColor];
    mLayer.lineWidth = 12.0;
    mLayer.lineCap = kCALineCapSquare;
    mLayer.path = [[UIBezierPath bezierPathWithRect: mLayer.bounds] CGPath]; // This can be whatever.

    [self.view.layer addSublayer: mLayer];

    [CATransaction begin];
    {
        [CATransaction setAnimationDuration: duration];//Dynamic Duration
        [CATransaction setCompletionBlock:^{ NSLog(@"Done"); }];

        const double portion = 1.0 / ((double)numSegments);

        for (NSUInteger i = 0; i < numSegments; i++)
        {
            CAShapeLayer* strokePart = [[CAShapeLayer alloc] init];
            strokePart.fillColor = [[UIColor clearColor] CGColor];
            strokePart.frame = mLayer.bounds;
            strokePart.path = mLayer.path;
            strokePart.lineCap = mLayer.lineCap;
            strokePart.lineWidth = mLayer.lineWidth;

            // These could come from an array or whatever, this is just easy...
            strokePart.strokeColor = [[UIColor colorWithHue: i * portion saturation:1 brightness:1 alpha:1] CGColor];
            strokePart.strokeStart = i * portion;
            strokePart.strokeEnd = (i + 1) * portion;

            [mLayer addSublayer: strokePart];

            CAKeyframeAnimation* animation = [CAKeyframeAnimation animationWithKeyPath: @"strokeEnd"];
            NSArray* times = @[ @(0.0), // Note: This works because both the times and the stroke start/end are on scales of 0..1
                                @(strokePart.strokeStart),
                                @(strokePart.strokeEnd),
                                @(1.0) ];
            NSArray* values = @[ @(strokePart.strokeStart),
                                 @(strokePart.strokeStart),
                                 @(strokePart.strokeEnd),
                                 @(strokePart.strokeEnd) ];

            animation.keyTimes = times;
            animation.values = values;
            animation.removedOnCompletion = NO;
            animation.fillMode = kCAFillModeForwards;
            [strokePart addAnimation: animation forKey: @"whatever"];
        }
    }
    [CATransaction commit];
}

@end
Run Code Online (Sandbox Code Playgroud)

我不确定我是否完全理解你的目的,但希望其中一个是有帮助的.