CABasicAnimation начать с текущего положения слоя

Это моя вторая неделя программирования Obj-C, и я столкнулся с небольшой проблемой с анимацией.

Я использую эту анимацию:

 CABasicAnimation *fullRotation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
    fullRotation.toValue = [NSNumber numberWithFloat:((360*M_PI)/180)];
    fullRotation.duration = 4;
    fullRotation.repeatCount= 1000;
    [[stick layer] addAnimation:fullRotation forKey:@"60"];}

эта анимация начинается при запуске моего приложения, затем я нажимаю некоторые кнопки, которые изменяют продолжительность анимации при нажатии, но новые анимации (которые имеют тот же код, но с разной длительностью) начинаются с исходного положения изображения "палка". Что я могу сделать, чтобы другие анимации начинались с текущего положение ручки, которая делает повороты на 360 градусов? Спасибо.

часть кода для более подробного объяснения:

-(void)viewDidAppear:(BOOL)animated{
    CABasicAnimation *fullRotation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
    fullRotation.toValue = [NSNumber numberWithFloat:((360*M_PI)/180)];
    fullRotation.duration = 4;
    fullRotation.repeatCount= 1000;
    [[stick layer] addAnimation:fullRotation forKey:@"60"];}

- (IBAction)button1:(UIButton *)sender {

 CABasicAnimation *fullRotation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
    fullRotation.toValue = [NSNumber numberWithFloat:((360*M_PI)/180)];
    fullRotation.duration = 6;
    fullRotation.repeatCount= 1000;
    [[stick layer] addAnimation:fullRotation forKey:@"60"];}

1 ответов


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

что происходит

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

только задание toValue для CABasicAnimation означает то, что он анимирует из текущего значения модели и указанного в значение. Обратите внимание, что в документации говорится, что это текущее значение презентации, но это на самом деле неверно. Поскольку значение модели никогда не изменяется, это означает, что при добавлении второй анимации она анимируется из неизмененного значения модели в toValue.

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

различные способы его исправления

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

добавление явного fromValue

как упоминалось выше, только с ненулевым toValue анимация будет начинаться от текущей стоимости. Вы может, однако, добавить non-nil fromValue это текущее значение презентации.

вы можете получить значение презентации из слоя presentationLayer. Из него вы можете получить текущий угол поворота, используя тот же ключевой путь, который вы анимируете, и немного KVC (кодирование ключевых значений):

NSNumber *currentAngle = [switch.layer.presentationLayer valueForKeyPath:@"transform.rotation"];

если бы вы только установили это как fromValue анимации вы получите правильное значение, но вращения не может быть больше 360 градусов. В то время как вы могли бы выяснить угол и создать новый toValue, есть еще одно свойство с именем byValue который производит относительное изменение. Указание byValue и fromValue (но не toValue) означает:

интерполирует между fromValue и (fromValue + byValue).

Итак, вы можете изменить код анимации на:

CABasicAnimation *fullRotation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
fullRotation.fromValue = currentAngle; // the value read from the presentation layer
fullRotation.byValue   = @(2.0*M_PI);
fullRotation.duration  = 6.0;

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

fullRotation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];

это должно дать вам поведение, которое вы ищете. Некоторые стилистические замечания были бы указать либо INFINITY или HUGE_VALF для отсчета повторения, если вы не хотите, чтобы он вращался ровно 1000 раз, и использовать более описательный ключ при добавлении слоя (если вы не используете Ключ для чего-то еще (например, для удаления анимации из слоя):

fullRotation.repeatCount    = INFINITY;
[stick.layer addAnimation:fullRotation forKey:@"rotate continuously"];

изменение скорости слоя

я бы дважды подумал, прежде чем использовать это в качестве решения в производственном коде, но это может быть весело в качестве учебного упражнения. Поскольку то, что вы в основном делаете, замедляет анимацию, изменяя продолжительность от 4 секунд до 6 секунд, вы можете сделать одну вещь для создания же эффект заключается в замедлении слоя. Обратите внимание, что это будет иметь побочные эффекты (вся анимация на этом слое и все его подслои также будут затронуты).

вы не можете изменить анимацию после ее добавления в слой, но сам слой также соответствует CAMediaTiming протокол, который означает, что он имеет speed собственность. Установка скорости в 4.0/6.0 и сохранение старой анимации на слое замедлит его, делая каждое вращение занимает 6 секунд вместо 4.

еще раз, немного хаки, но весело, как учебное упражнение.