CABasicAnimation мерцает при применении завершения

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

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

Вот код, который я сейчас использую:

if (futureAngle == currentAngle) {
    return;
}

float rotationAngle;
if (futureAngle < currentAngle) {
    rotationAngle = futureAngle - currentAngle;
}else{
    rotationAngle = futureAngle - currentAngle;
}

float animationDuration = fabs(rotationAngle) / 100;
rotationAngle = GLKMathDegreesToRadians(rotationAngle);

[CATransaction begin];
CABasicAnimation *rotationAnimation;
rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
rotationAnimation.byValue = [NSNumber numberWithFloat:rotationAngle];
rotationAnimation.duration = animationDuration;
rotationAnimation.removedOnCompletion = YES;

[CATransaction setCompletionBlock:^{
    view.transform = CGAffineTransformRotate(view.transform, rotationAngle);
}];

[view.layer addAnimation:rotationAnimation forKey:@"rotationAnimation"];
[CATransaction commit];

person Laur Stefan    schedule 28.06.2016    source источник
comment
Когда вы говорите мерцание, я полагаю, вы имеете в виду, что в конце анимации она на мгновение возвращается в исходное состояние, прежде чем вернуться в конечное состояние? Это можно решить либо (а) установив окончательный view.transform перед запуском анимации (и вам больше не нужен completionBlock); или (b) установив fillMode анимации на kCAFillModeForwards и установив removedOnCompletion на false.   -  person Rob    schedule 28.06.2016
comment
@Rob, можете ли вы привести пример предложения относительно блока завершения? Я попробовал fillMode на kCAFillModeForwards и установил для removeOnCompletion значение false перед созданием сообщения, и они у меня не сработали.   -  person Laur Stefan    schedule 29.06.2016


Ответы (2)


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

  • установка конечного view.transform перед тем, как вы начнете анимацию (и вам больше не нужен completeBlock);
  • установив fillMode анимации на kCAFillModeForwards и установив removedOnCompletion на false.

Лично я считаю, что установка анимированного свойства в его целевое значение до запуска анимации — это самый простой способ сделать это.

Таким образом:

- (void)rotate:(UIView *)view by:(CGFloat)delta {
    float animationDuration = 2.0;
    CGFloat currentAngle = self.angle;                                                    // retrieve saved angle
    CGFloat nextAngle = self.angle + delta;                                               // increment it
    self.angle = nextAngle;                                                               // save new value

    view.transform = CGAffineTransformMakeRotation(nextAngle);                            // set property to destination rotation

    CABasicAnimation *rotationAnimation;
    rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];  // now rotate
    rotationAnimation.fromValue = @(currentAngle);
    rotationAnimation.toValue = @(nextAngle);
    rotationAnimation.duration = animationDuration;
    rotationAnimation.removedOnCompletion = YES;
    [view.layer addAnimation:rotationAnimation forKey:@"rotationAnimation"];
}

Или, думаю, еще проще, просто настроить transform:

- (void)rotate:(UIView *)view by:(CGFloat)delta {
    float animationDuration = 2.0;
    CGAffineTransform transform = view.transform;                                         // retrieve current transform
    CGAffineTransform nextTransform = CGAffineTransformRotate(transform, delta);          // increment it

    view.transform = nextTransform;                                                       // set property to destination rotation

    CABasicAnimation *rotationAnimation;
    rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform"];             // now rotate
    rotationAnimation.fromValue = [NSValue valueWithCGAffineTransform:transform];
    rotationAnimation.toValue = [NSValue valueWithCGAffineTransform:nextTransform];
    rotationAnimation.duration = animationDuration;
    rotationAnimation.removedOnCompletion = YES;
    [view.layer addAnimation:rotationAnimation forKey:@"rotationAnimation"];
}
person Rob    schedule 29.06.2016
comment
Удивительно, может показаться, что если вы одновременно пытаетесь выполнить UIView.animate (скажем, просто перемещаете ограничение), подход fillMode кажется ответом...... ... - person Fattie; 17.08.2017
comment
Спасибо Роб! Почему-то первый вариант мне не подходит, а второй работает. - person CodeBrew; 03.04.2021

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

person Fonix    schedule 06.10.2020
comment
Боже мой, ты буквально сэкономил мне кучу времени. На симуляторе анимация мерцает/мигает, а на реальном устройстве просто работает. Мне потребовалось некоторое время, чтобы понять, что, возможно, в моем коде нет проблемы, а решение лежит где-то в другом месте. Очень-очень большое спасибо! - person jason d; 09.07.2021