Измените цвет CALayer во время анимации

ПОДХОД 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];

Я добавил CAShapeLayer (pathLayer) в свое представление, и я хочу, чтобы он анимировал вокруг представления с эффектом обводки, приведенный выше код выполняет свою работу, но моя проблема состоит в том, чтобы изменить цвет в трех равных пропорциях. Итак, я предполагаю повторить приведенный выше код 3 раза и изменить следующие строки в соответствующем порядке.

для 1-го

    animation.fromValue = @0;
    animation.toValue = @(1/3);
    animation.timeOffset = 0;

для 2-го

    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.

для 3-го

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

ПОДХОД 2

Вместо 3-х транзакций со смещением можно начинать вторую транзакцию по завершении первой и третью по завершении второй. Но часть времени, необходимая для запуска новой анимации, когда она завершена, становится заметной.

ПОДХОД 3

Подкласс CAShapeLayer

При выполнении SubClass метод drawInContext вызывается только один раз, и если какое-то дополнительное свойство добавляется и изменяется, метод drawInContext вызывается повторно, и таким образом цвет слоя может быть изменен по истечении определенного периода времени. Но переопределение метода drawInContext не служит цели.

Какие-либо предложения ? Я не хочу реализовывать NSTimer отдельно.


person yunas    schedule 06.09.2013    source источник


Ответы (1)


Я не на 100% понимаю, что вы хотите здесь, но если цель состоит в том, чтобы изменить цвет всего штриха в процессе рисования, но в три отдельных этапа, то я бы предложил добавить следующее. Я приготовил эти примеры в шаблоне «Single View Application» по умолчанию. У меня настроена кнопка, действие которой указывает на -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

В качестве альтернативы, если цель состоит в том, чтобы иметь три разных сегмента обводки, все разного цвета, это немного сложнее, но все же может быть выполнено с одними и теми же основными принципами. Следует отметить, что без пользовательского рисунка ваши 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

Я не уверен, что точно понял, что вы собирались сделать, но, надеюсь, один из них будет вам полезен.

person ipmcc    schedule 06.09.2013
comment
Спасибо за ответ, это совершенно новая перспектива для меня, чтобы увидеть, как красиво мы можем играть с анимацией слоев. Я реализовал это, и единственная неправильная вещь в моей реализации была на шаге 3, который я сделал, animation.toValue = @ (3); предполагалось, что это animation.toValue = @ (1); как бы то ни было, я бы хотел, чтобы вы добавили способ, которым вы реализовали ответ, который изменит цвет всего слоя на шаге 2, а затем цвет всего слоя на шаге 3. Спасибо - person yunas; 06.09.2013
comment
@yunas отредактирован, чтобы дать более подробную информацию о примере изменения цвета всего штриха. - person ipmcc; 06.09.2013
comment
@ipmcc, как мы могли в этом примере постепенно менять цвета обводки с одного на другой, почти как градиент? Спасибо. - person Unheilig; 12.10.2013
comment
@Unheilig: Это намного сложнее. Говоря абстрактно, вы, вероятно, захотите очертить контур обводки, поместить его в слой формы, затем разделить эту форму на сегменты, представляющие равные по длине части исходного, не очерченного контура (нетривиально для произвольных кубических путей Безье), а затем заполните каждый подраздел соответствующим субградиентом, затем добавьте еще один слой-фигуру с простой черной обводкой в ​​качестве слоя маски. Это будет непросто или с точки зрения вычислений. - person ipmcc; 12.10.2013
comment
Например, Adobe Illustrator, который существует с 1987 года, потребовалось четверть века, чтобы разработать градиент вдоль штрихов (функция появилась в CS6 в 2012 году), и я почти уверен, что это не потому, что никто об этом не подумал. до того... - person ipmcc; 12.10.2013
comment
и как сделать переход цвета более плавным? - person Ilker Baltaci; 08.02.2014
comment
Спрашивали и отвечали в других комментариях. - person ipmcc; 08.02.2014
comment
Как мне изменить цвета с красного, зеленого, синего на свои собственные? - person Faisal Syed; 02.02.2017