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

Анимация UIView с автореверсированием

У меня проблема с настройкой UIViewAnimationOptionAutoReverse. Вот мой код.

CALayer *aniLayer = act.viewToChange.layer;
[UIView animateWithDuration:2.0 delay:1.0 options:(UIViewAnimationCurveLinear | UIViewAnimationOptionAutoreverse) animations:^{
                    viewToAnimate.frame = GCRectMake(1,1,100,100);
                    [aniLayer setValue:[NSNumber numberWithFloat:degreesToRadians(34)] forKeyPath:@"transform.rotation"];
                } completion:nil ];

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

Есть ли решение без программирования двух последовательных анимаций?

4b9b3361

Ответ 1

У вас есть три варианта.

При использовании методов -[UIView animateWithDuration:…] изменения, внесенные вами в блок animations, немедленно применяются к рассматриваемым представлениям. Однако также существует неявный CAAnimation, примененный к представлению, которое анимируется от старого значения до нового значения. Когда a CAAnimation активен в представлении, он изменяет отображаемое представление, но не изменяет фактические свойства представления.

Например, если вы это сделаете:

NSLog(@"old center: %@", NSStringFromCGPoint(someView.center));
[UIView animateWithDuration:2.0 animations: ^{ someView.center = newPoint; }];
NSLog(@"new center: %@", NSStringFromCGPoint(someView.center));

вы увидите, что "старый центр" и "новый центр" различны; новый центр сразу отразит значения newPoint. Однако CAAnimation, который был неявно создан, заставит представление все еще отображаться в старом центре и плавно переходить к новому центру. Когда анимация закончится, она будет удалена из представления и вы вернетесь к простому отображению фактических значений модели.

Когда вы передаете UIViewAnimationOptionAutoreverse, это влияет на неявно созданный CAAnimation, но НЕ влияет на фактическое изменение, которое вы делаете на значения. То есть, если наш пример выше имел значение UIViewAnimationOptionAutoreverse, то неявно созданный CAAnimation мог бы анимироваться от oldCenter до newCenter и обратно. Затем анимация будет удалена, и мы вернемся к просмотру значений, которые мы установили... , которые все еще находятся в новой позиции.

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

Первый вариант

CGPoint oldCenter = someView.center;
[UIView animateWithDuration:2.0
                 animations: ^{ someView.center = newPoint; }
                 completion: 
   ^(BOOL finished) {
       [UIView animateWithDuration:2.0
                        animations:^{ someView.center = oldCenter; }];
   }];

Второй вариант

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

CGPoint oldCenter = someView.center;
[UIView animateWithDuration:2.0
                      delay:0
                    options: UIViewAnimationOptionAutoreverse
                 animations: ^{ someView.center = newPoint; }
                 completion: ^(BOOL finished) { someView.center = oldCenter; }];

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

Третий вариант

Последний вариант - просто создать CAAnimation напрямую. Когда вы фактически не хотите изменять конечное значение свойства, которое вы меняете, это часто бывает проще.

#import <QuartzCore/QuartzCore.h>

CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
animation.autoreverses = YES;
animation.repeatCount = 1; // Play it just once, and then reverse it
animation.toValue = [NSValue valueWithCGPoint:newPoint];
[someView.layer addAnimation:animation forKey:nil];

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

Способ CAAnimation также требует, чтобы вы добавили его в представление, лежащее в основе CALayer. Если это вас пугает, не стесняйтесь использовать методы -[UIView animateWithDuration:…]. Доступны дополнительные функции с помощью CAAnimation, но если это то, что вы не знакомы, целая анимация UIView или ее сброс в блоке завершения вполне приемлемы. На самом деле, это одна из основных целей блока завершения.

Итак, вы идете. Три разных способа изменить анимацию и сохранить исходное значение. Наслаждайтесь!

Ответ 2

Вот мое решение. Для 2x повторите, анимируйте 1.5x и сделайте последнюю часть 0.5x самостоятельно:

[UIView animateWithDuration:.3
                      delay:.0f
                    options:(UIViewAnimationOptionRepeat|
                             UIViewAnimationOptionAutoreverse)
                 animations:^{
                     [UIView setAnimationRepeatCount:1.5f];

                     ... animate here ...

                 } completion:^(BOOL finished) {
                         [UIView animateWithDuration:.3 animations:^{

                             ... finish the animation here ....

                         }];
                 }];

Не мигает, работает хорошо.

Ответ 3

В CAMediaTiming есть свойство repeatCount. Я думаю, вам нужно создать явный объект CAAnimation и настроить его правильно. Для повторного набора для grow and ungrow будет 2, но вы можете проверить это.

Что-то длинное. Вам понадобится два объекта CAAnimation: один для кадра и один для вращения.

CABasicAnimation *theAnimation;

theAnimation=[CABasicAnimation animationWithKeyPath:@"transform.rotation"];
theAnimation.duration=3.0;
theAnimation.repeatCount=1;
theAnimation.autoreverses=YES;
theAnimation.fromValue=[NSNumber numberWithFloat:0.0];
theAnimation.toValue=[NSNumber numberWithFloat:degreesToRadians(34)];
[theLayer addAnimation:theAnimation forKey:@"animateRotation"];

AFAIK нет способа избежать программирования двух объектов анимации.