Как дождаться, пока повторяющийся CAAnimation завершит цикл, прежде чем удалять его

#ios #core-animation #caanimation

#iOS #ядро-анимация #caanimation

Вопрос:

У меня есть представление, которое я позволяю пульсировать, используя CAAnimation .

 CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"opacity"];
animation.values = @[ @0.0f, @1.0f, @0.0f ];
animation.duration = 0.5;
animation.repeatCount = HUGE_VALF;

[view.layer addAnimation:animation forKey:@"pulsate"];
  

Когда я удаляю анимацию с использованием [view.layer removeAnimationForKey:@"pulsate"] непрозрачности, она немедленно возвращается. Чего я хотел бы добиться, так это того, что текущая выполняемая пульсирующая анимация завершена, а затем анимация удаляется.

Я попытался, установив значение repeatCount 1, но это вызывает исключение, потому что анимация неизменна.

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

Есть ли способ позволить анимации завершить цикл и удалить ее после этого?

Ответ №1:

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

Первым делом необходимо объявить некоторые свойства

 @property (weak, nonatomic) IBOutlet UIImageView *orangeView2;
@property (nonatomic) bool pulseActive;
@property (strong, nonatomic) CAKeyframeAnimation *pulseAnimation;
  

Первое свойство — это представление, которое будет анимировано, второе отслеживает, включена ли анимация, а последнее — фактическая анимация (хранится в property, так что нам нужно создать его экземпляр только один раз).

Далее мы будем использовать отложенное создание экземпляра для создания объекта анимации

 - (CAKeyframeAnimation *)pulseAnimation
{
    if ( !_pulseAnimation )
    {
        _pulseAnimation = [CAKeyframeAnimation animationWithKeyPath:@"opacity"];
        _pulseAnimation.values = @[ @0.0f, @1.0f, @0.0f ];
        _pulseAnimation.duration = 0.5;

        _pulseAnimation.delegate = self;
        [_pulseAnimation setValue:@"PulseAnimation" forKey:@"AnimationIdentifier"];
    }

    return( _pulseAnimation );
}
  

Важные моменты здесь

  • анимация не повторяется (по умолчанию)
  • анимация removedOnCompletion (по умолчанию)
  • значение delegate установлено self так, что animationDidStop будет вызван метод
  • анимации присваивается идентификатор с помощью setValue:forKey:

Этот последний элемент необходим только в том случае, если несколько анимаций используют один и тот же делегат, поскольку в этом случае вам понадобится способ определить, какая анимация вызвана animationDidStop . Строки, передаваемые в forKey и setValue , являются произвольными и хранятся в словаре в объекте анимации.

Хорошо, теперь нам нужно реализовать animationDidStop . Реализация проверяет pulseActive свойство и при необходимости перезапускает анимацию (после проверки подлинности анимации).

 - (void)animationDidStop:(CAAnimation *)animation finished:(BOOL)flag
{
    NSString *animationIdentifier = [animation valueForKey:@"AnimationIdentifier"];

    if ( [animationIdentifier isEqualToString:@"PulseAnimation"] )
    {
        if ( self.pulseActive )
            [self.orangeView2.layer addAnimation:self.pulseAnimation forKey:@"pulsate"];
    }
}
  

Все, что осталось, это запустить и остановить анимацию. Например, кнопка, которая переключает анимацию

 - (IBAction)pulseButtonPressed
{
    if ( !self.pulseActive )
    {
        self.pulseActive = YES;
        [self.orangeView2.layer addAnimation:[self pulseAnimation] forKey:@"pulsate"];
    }
    else
    {
        self.pulseActive = NO;
    }
}
  

Комментарии:

1. Для меня повторное использование анимации не сработало должным образом, однако добавление новой работает отлично. Спасибо!