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

Как связать различные CAAnimation в приложении iOS

Мне нужно связать анимации, CABasicAnimation или CAAnimationGroup, но я не знаю, как это сделать, единственное, что я делаю, это то, что все анимации выполняются одновременно для одного и того же слоя.

Как я могу это сделать?

Например, слой с его содержимым, установленным на изображение автомобиля:

1st: переместите X точек вправо

2nd: поверните 90ª влево

3rd: Move X point

4th: масштабируйте слой

Все эти анимации должны выполняться по-второму, но я не могу этого сделать: S

Кстати: я не английский, извините, если я допустил некоторые ошибки в своей грамматике: D

4b9b3361

Ответ 1

tl; dr: Вам нужно вручную добавить каждую анимацию после предыдущих финишей.


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

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

В этом методе вы удалите из массива первую анимацию (что означает следующую анимацию) и добавьте ее в слой. Поскольку вы являетесь делегатом, вы получите вторую анимацию, когда она закончится, и в этом случае обратный вызов animationDidStop:finished: снова запустится, а следующая анимация будет удалена из изменяемого массива и добавлена ​​в этот слой.

Когда массив анимаций пуст, все анимации будут запущены.


Некоторые примеры кода, чтобы вы начали. Сначала вы настроили все свои анимации:

CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"backgroundColor"];
[animation setToValue:(id)[[UIColor redColor] CGColor]];
[animation setDuration:1.5];
[animation setDelegate:self];
[animation setValue:[view layer] forKey:@"layerToApplyAnimationTo"];

// Configure other animations the same way ...

[self setSequenceOfAnimations:[NSMutableArray arrayWithArray: @[ animation, animation1, animation2, animation3, animation4, animation5 ] ]];

// Start the chain of animations by adding the "next" (the first) animation
[self applyNextAnimation];

Затем в обратном вызове делегата вы просто снова применяете следующую анимацию

- (void)animationDidStop:(CAAnimation *)animation finished:(BOOL)finished {
    [self applyNextAnimation];
}

- (void)applyNextAnimation {
     // Finish when there are no more animations to run
    if ([[self sequenceOfAnimations] count] == 0) return;

    // Get the next animation and remove it from the "queue"
    CAPropertyAnimation * nextAnimation = [[self sequenceOfAnimations] objectAtIndex:0];
    [[self sequenceOfAnimations] removeObjectAtIndex:0];

    // Get the layer and apply the animation
    CALayer *layerToAnimate = [nextAnimation valueForKey:@"layerToApplyAnimationTo"];
    [layerToAnimate addAnimation:nextAnimation forKey:nil];
}

Я использую настраиваемый ключ layerToApplyAnimationTo, чтобы каждая анимация знала свой слой (он работает только под setValue:forKey: и valueForKey:).

Ответ 2

Что предлагает Дэвид, отлично работает, но я бы порекомендовал другой способ.

Если все ваши анимации находятся на одном уровне, вы можете создать группу анимации и сделать каждую анимацию другой beginTime, где первая анимация начинается с beginTime 0, и каждая новая анимация начинается после общего продолжительность анимации раньше.

Если ваши анимации находятся на разных уровнях, вы не можете использовать группы анимации (все анимации в группе анимации должны действовать на одном уровне). В этом случае вам нужно представить отдельные анимации, каждая из которых имеет beginTime, который смещен от CACurrentMediaTime(), например:

CGFloat totalDuration = 0;
CABasicAnimation *animationOne = [CABasicAnimation animationWithKeyPath: @"alpha"];
animationOne.beginTime = CACurrentMediaTime(); //Start instantly.
animationOne.duration = animationOneDuration;
...
//add animation to layer

totalDuration += animationOneDuration;

CABasicAnimation *animationTwo = [CABasicAnimation animationWithKeyPath: @"position"];
animationTwo.beginTime = CACurrentMediaTime() + totalDuration; //Start after animation one.
animationTwo.duration = animationTwoDuration;
...
//add animation to layer

totalDuration += animationTwoDuration;


CABasicAnimation *animationThree = [CABasicAnimation animationWithKeyPath: @"position"];
animationThree.beginTime = CACurrentMediaTime() + totalDuration; //Start after animation three.
animationThree.duration = animationThreeDuration;
...
//add animation to layer

totalDuration += animationThreeDuration;

Ответ 3

Либо подход Дэвида, либо свойство beginTime подходят для цепочки анимации. См. http://wangling.me/2011/06/time-warp-in-animation.html - в нем разъясняется использование свойств beginTime и других CAMediaTiming.

Ответ 4

Здесь решение в Swift:

var animations = [CABasicAnimation]()

var animation1 = CABasicAnimation(keyPath: "key_path_1")
// animation set up here, I've included a few properties as an example
animation1.duration = 1.0
animation1.fromValue = 1
animation1.toValue = 0
animation1.append(animation1)

var animation2 = CABasicAnimation(keyPath: "key_path_2")
animation2.duration = 1.0
animation2.fromValue = 1
animation2.toValue = 0
// setting beginTime is the key to chaining these
animation2.beginTime = 1.0
animations.append(animation2)

let group = CAAnimationGroup()
group.duration = 2.0
group.repeatCount = FLT_MAX
group.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
group.animations = animations

yourLayer.addAnimation(group, forKey: nil)

Ответ 5

Используйте KVC. setValue для ключа для каждой анимации. После этого в animationDidStop вы можете определить, какая анимация была остановлена ​​и запущена дальше в цепочке.

- (void)moveXrightAnimation {
    CABasicAnimation* theAnimation = ...
    [theAnimation setValue:@"movexright" forKey:@"animationID"];
//animation
}

- (void)rotate90leftAnimation {
    CABasicAnimation* theAnimation = ...
    [theAnimation setValue:@"rotate90left" forKey:@"animationID"];
//animation
}

- (void)moveXpointAnimation {
    CABasicAnimation* theAnimation = ...
    [theAnimation setValue:@"movexpoint" forKey:@"animationID"];
//animation
}

- (void)scaleAnimation {
    CABasicAnimation* theAnimation = ...
    [theAnimation setValue:@"scale" forKey:@"animationID"];
//animation
}

#pragma mark - CAAnimationDelegate

- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
    
    if([[anim valueForKey:@"animationID"] isEqual:@"movexright"]) {
        [self rotate90leftAnimation];
    }
    if([[anim valueForKey:@"animationID"] isEqual:@"rotate90left"]) {
        [self moveXpointAnimation];
    }
    if([[anim valueForKey:@"animationID"] isEqual:@"movexpoint"]) {
        [self scaleAnimation];
    }
}