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

Использование CABasicAnimation для поворота UIImageView более одного раза

Я использую CABasicAnimation для поворота a UIImageView на 90 градусов по часовой стрелке, но мне нужно, чтобы он вращался еще на 90 градусов позже от своего положения после начального поворота на 90 градусов.

CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
animation.duration = 10;
animation.additive = YES;
animation.removedOnCompletion = NO;
animation.fillMode = kCAFillModeForwards;
animation.fromValue = [NSNumber numberWithFloat:DEGREES_TO_RADIANS(0)];
animation.toValue = [NSNumber numberWithFloat:DEGREES_TO_RADIANS(90)];
[_myview.layer addAnimation:animation forKey:@"90rotation"];

Использование вышеприведенного кода работает первоначально, изображение остается под углом 90 градусов. Если я снова назову это, чтобы он повернул еще на 90 градусов, анимация начнется, вернувшись на 0 и повернув на 90 градусов, а не на 90... 180 градусов.

У меня создалось впечатление, что animation.additive = YES; приведет к тому, что дальнейшие анимации будут использовать текущее состояние в качестве отправной точки.

Любые идеи?

4b9b3361

Ответ 1

передать инкрементное значение угла см. мой код

static int imgAngle=0;
- (void)doAnimation
{
  CABasicAnimation *animation = [CABasicAnimation   animationWithKeyPath:@"transform.rotation.z"];
   animation.duration = 5;
  animation.additive = YES;
  animation.removedOnCompletion = NO;
  animation.fillMode = kCAFillModeForwards;
  animation.fromValue = [NSNumber numberWithFloat:DEGREES_TO_RADIANS(imgAngle)];
  animation.toValue = [NSNumber numberWithFloat:DEGREES_TO_RADIANS(imgAngle+90)];
 [self.imgView.layer addAnimation:animation forKey:@"90rotation"];

  imgAngle+=90;
  if (imgAngle>360) {
      imgAngle = 0;
  }
}

Выше код предназначен только для идеи. Его не проверено

Ответ 2

tl; dr: Очень легко злоупотреблять removeOnCompletion = NO, и большинство людей не понимают последствий этого. Правильное решение - изменить значение модели "для реального".


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

Что пошло не так

У меня создалось впечатление, что animation.additive = YES; приведет к тому, что дальнейшие анимации будут использовать текущее состояние в качестве отправной точки.

Это очень верно, и именно это происходит. В этом смысле компьютеры смешны. Они всегда точно, что вы им говорите, а не то, что вы хотите от них делать.

removeOnCompletion = NO может быть сукой

В вашем случае злодей - это строка кода:

animation.removedOnCompletion = NO;

Часто не рекомендуется использовать окончательное значение анимации после завершения анимации. Единственная проблема заключается в том, что это происходит, не удаляя анимацию из представления. Анимации в Core Animation не изменяют базовое свойство, которое они оживляют, они просто оживляют его на экране. Если вы посмотрите на фактическое значение во время анимации, вы никогда не увидите, как это изменится. Вместо этого анимация работает над тем, что называется уровнем представления.

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

Когда вы настраиваете анимацию как аддитивную, это означает, что значения from и to добавляются к существующему значению, как вы сказали. Проблема в том, что значение этого свойства равно 0. Вы никогда его не изменяете, вы просто анимируете его. В следующий раз, когда вы попытаетесь добавить эту анимацию на один и тот же слой, значение все равно не будет изменено, но анимация делает именно то, что он настроил для выполнения: "анимировать аддитивно от текущего значения модели".

Решение

Пропустить эту строку кода. В результате, однако, вращение не прилипает. Лучший способ сделать это - изменить модель. Установите новое конечное значение вращения перед анимацией вращения, чтобы модель выглядела так, как должна, когда анимация удаляется.

byValue - это как магия

В CABasicAnimation есть очень удобное свойство (которое я буду использовать), которое называется byValue, которое можно использовать для создания относительных анимаций. Его можно комбинировать с toValue и fromValue, чтобы делать много разных видов анимаций. Различные комбинации указаны в его документации (в разделе). Комбинация, которую я собираюсь использовать:

byValue и toValue не являются nil. Интерполирует между (toValue - byValue) и toValue.

Некоторый фактический код

При явном toValue из 0 анимация происходит от "currentValue-byValue" до "текущего значения". Изменяя модель, первое значение тока - это конечное значение.

NSString *zRotationKeyPath = @"transform.rotation.z"; // The killer of typos

// Change the model to the new "end value" (key path can work like this but properties don't)
CGFloat currentAngle = [[_myview.layer valueForKeyPath:zRotationKeyPath] floatValue];
CGFloat angleToAdd   = M_PI_2; // 90 deg = pi/2
[_myview.layer setValue:@(currentAngle+angleToAdd) forKeyPath:zRotationKeyPath]; 

CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:zRotationKeyPath];
animation.duration = 10;
// @( ) is fancy NSNumber literal syntax ...
animation.toValue = @(0.0);        // model value was already changed. End at that value
animation.byValue = @(angleToAdd); // start from - this value (it toValue - byValue (see above))

// Add the animation. Once it completed it will be removed and you will see the value
// of the model layer which happens to be the same value as the animation stopped at.
[_myview.layer addAnimation:animation forKey:@"90rotation"];

Небольшая оговорка:

Я не запускал этот код, но достаточно уверен, что он работает так, как должен, и что я не делал никаких опечаток. Исправьте меня, если я это сделаю. Все обсуждение остается в силе.