Подтвердить что ты не робот

Цепи анимации анимации ядра

Какой самый элегантный и модульный способ анимации в анимации в контексте Core Animation?

Я хочу сделать анимацию, которая начинается только тогда, когда другая закончена (например, меняя position, а затем opacity).. Обычный подход заключается в прямом изменении свойств:

layer.position = new_point;
layer.opacity = 0.0f;

но это будет делать их одновременно. Я хочу, чтобы один подождал другого.

А как насчет цепочки анимации для разных объектов? Я читал о CATransaction, используемом как:

[CATransaction begin]
layer1.property = new_property;
[CATransaction begin]
layer2.property2 = new_property2;
[CATransaction commit];
[CATransaction commit];

но он, похоже, не работает.

4b9b3361

Ответ 1

Вы также можете использовать группировку анимации и использовать поле beginTime анимации. Попробуйте что-то вроде этого:

CABasicAnimation *posAnimation = [CABasicAnimation animationWithKeyPath:@"position"];
[posAnimation setFromValue:[NSNumber numberWithFloat:0.0]];
[posAnimation setToValue:[NSNumber numberWithFloat:1.0]];
// Here the important part
[posAnimation setDuration:10.0];
[posAnimation setBeginTime:0.0];

CABasicAnimation *borderWidthAnimation = [CABasicAnimation animationWithKeyPath:@"borderWidth"];
[borderWidthAnimation setFromValue:[NSNumber numberWithFloat:0.0]];
[borderWidthAnimation setToValue:[NSNumber numberWithFloat:1.0]];
// Here the important part
[borderWidthAnimation setDuration:10.0];
[borderWidthAnimation setBeginTime:5.0];

CAAnimationGroup *group = [CAAnimationGroup animation];
[group setDuration:10.0];
[group setAnimations:[NSArray arrayWithObjects:posAnimation, borderWidthAnimation, nil]];

[layer addAnimation:group forKey:nil];

Обратите внимание, что продолжительность всей анимации составляет 10 секунд. Первый начинается со второго 0, а второй начинается через 5 секунд.

Ответ 2

Как отметил Мэтт, вы можете создавать анимационные группы, которые состоят из разных анимаций для одного и того же слоя с разными временами начала. Вы также можете установить делегат для автономных объектов CAAnimation или групп CAAnimation, и по мере окончания каждой анимации он вызовет метод делегирования animationDidStop:finished: (обратите внимание, что анимации, входящие в группу, не будут вызывать их делегат animationDidStop:finished:.

Я понял, какой классный трюк использует метод CAAnimation animationDidStop:finished: более мощный. Я использую метод setValue:forKey:, чтобы добавить блок кода в отдельную анимационную или анимационную группу с ключом @ "animationCompletionBlock". Затем я пишу общий метод animationDidStop:finished:, который проверяет только что завершенную анимацию для ключа @ "animationCompletionBlock", и если он ее найдет, выполните там блок кода.

Взгляните на этот проект на github для рабочего примера этого метода:

демо CAAnimation с блоками завершения

Вы также установите группу анимаций внутри

[CATransaction begin];
//...
[[CATransaction commit];

как вы сказали. Когда вы это сделаете, вы можете использовать метод класса CATransaction setCompletionBlock: для вызова блока кода, когда все анимации в текущей группе транзакций завершены. Блок завершения для одной транзакции может затем инициировать следующую транзакцию.

Ответ 4

То, что я всегда предпочитал устанавливать время начала и окончания каждой анимации отдельно, это:

Я использовал A2DynamicDelegate (развитие которого сейчас происходит в BlocksKit -Repo, кто знает, почему < _ <), чтобы реализовать свойство завершения блокировки в категории по CAAnimation.

Это позволило мне сделать так:

CAAnimation *a = ...
CAAnimation *b = ...
CAAnimation *c = ...

a.completionHandler = ^{
    [self.layer addAnimation:b forKey:@"foo"];
    [self.layer addAnimation:c forKey:@"bar"];
};

Гораздо более гибкий:)

Я загрузил свой код для обработчика завершения здесь. Однако обратите внимание на уведомление в файле заголовка. Я действительно смущен, почему метод не вызывается.

Ответ 5

Я вытащил это, используя метод setCompletionBlock, чтобы определить замыкание, которое запускает следующую анимацию, когда первая завершена:

[CATransaction begin]
layer1.property = new_property;
CATransaction.setCompletionBlock {
    [CATransaction begin]
    layer2.property2 = new_property2;
    [CATransaction commit];
}
[CATransaction commit];

Ответ 6

Без включения всех "трюков" в мою "инструментальную цепочку" этот пример не копируется напрямую/скоропортящийся... но он показывает ДЕЙСТВИТЕЛЬНО легкую стратегию "прикованных" анимаций.

CATransform3D trans = m34();  // define chain-wide constants.
// Name your "stack". My "nextObject" returns arr[i]->done == nil. 
NSArray *layerStack = layer.sublayers;
//define a block, that "takes" a layer as it argument.
void(^__block ChainBlock)(CALayer*) = ^(CALayer *m) { 
// animations, transforms, etc for each inividual "step".
    [m animate:@"transform" 
          // These are just NSValue-wrapped CAT3D's
          from:AZV3d(CATransform3DRotate(trans,  0,1,0,0)) 
            to:AZV3d(CATransform3DRotate(trans,1.5,1,0,0)) 
          time:2    // total time == each step * layerStack.count
         eased:kCAMediaTimingFunctionEaseOut
    completion:^{   // In completion, look for "next" layer.
                    CAL*  m2 = [layers nextObject]; 
// If there is "another" layer, call this block, again... with it.
                      if (m2)  chainAnis(m2);
// Otherise,you're done.  Cleanup, toggle values, whatevs.
                     else self.finishedProperty = YES;
    }];
};
// Give the block we just defined your "first" layer.
ChainBlock(layerStack[0]);  // It will recursively feed itself.

Это, очевидно, зависит от некоторой "внешней магии", но концепция проста и устраняет (через зависимости) необходимость "иметь дело" с ЛЮБЫМ видом валовой делегации. В частности, категории animate:from:to:time:easing:completion и т.д. Относятся к большой FunSize Framework, на Github.