web-dev-qa-db-fra.com

Animations UIView avec inversion automatique

J'ai un problème avec le paramètre UIViewAnimationOptionAutoReverse. Voici mon code.

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 ];

Le problème est que, une fois l'animation inversée, la vue revient à l'image définie dans le bloc d'animation. Je veux que la vue grandisse et se "détruise" et s'arrête à sa position d'origine.

Existe-t-il une solution sans programmer deux animations consécutives?

38
Stultus

Vous avez trois options.

Lorsque vous utilisez le -[UIView animateWithDuration:…], les modifications que vous apportez dans le bloc animations sont appliquées immédiatement aux vues en question. Cependant, il existe également un CAAnimation implicite appliqué à la vue qui anime de l'ancienne valeur à la nouvelle valeur. Lorsqu'un CAAnimation est actif sur une vue, il modifie la vue affichée , mais ne modifie pas les propriétés réelles de la vue.

Par exemple, si vous procédez ainsi:

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

vous verrez que "l'ancien centre" et le "nouveau centre" sont différents; nouveau centre reflétera immédiatement les valeurs de newPoint. Cependant, le CAAnimation qui a été implicitement créé fera que la vue sera toujours affichée à l'ancien centre et se déplacera en douceur vers le nouveau centre. Une fois l'animation terminée, elle est supprimée de la vue et vous revenez à voir uniquement les valeurs réelles du modèle.

Lorsque vous passez UIViewAnimationOptionAutoreverse, cela affecte le CAAnimation créé implicitement, mais n'affecte PAS la modification réelle que vous apportez aux valeurs. Autrement dit, si notre exemple ci-dessus avait le UIViewAnimationOptionAutoreverse défini, le CAAnimation implicitement créé s'animerait de oldCenter vers newCenter et vice-versa. L'animation serait alors supprimée, et nous reviendrions pour voir les valeurs que nous avons définies… qui est toujours à la nouvelle position.

Comme je l'ai dit, il y a trois façons de régler ce problème. La première consiste à ajouter un bloc d'achèvement sur l'animation pour l'inverser, comme ceci:

Première option

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

Deuxième option

La deuxième option consiste à inverser automatiquement l'animation comme vous le faites et à remettre la vue à sa position d'origine dans un bloc d'achèvement:

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

Cependant, cela peut provoquer un scintillement entre le moment où l'inverse de l'animation se termine et le moment où le bloc d'achèvement s'exécute, donc ce n'est probablement pas votre meilleur choix.

Troisième option

La dernière option consiste à créer simplement un CAAnimation directement. Lorsque vous ne souhaitez pas réellement modifier la valeur finale de la propriété que vous modifiez, cela est souvent plus simple.

#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];

Notez que la façon de faire CAAnimation ne change jamais les valeurs réelles de la vue; il masque simplement les valeurs réelles avec une animation. La vue pense toujours que c'est à l'emplacement d'origine. (Cela signifie, par exemple, que si votre vue répond aux événements tactiles, elle surveillera toujours ces événements tactiles à leur emplacement d'origine. L'animation uniquement change la façon dont la vue dessine, rien d'autre.

La méthode CAAnimation nécessite également que vous l'ajoutiez à la vue CALayer sous-jacente de la vue. Si cela vous fait peur, n'hésitez pas à utiliser le -[UIView animateWithDuration:…] méthodes à la place. Il y a des fonctionnalités supplémentaires disponibles en utilisant CAAnimation, mais si c'est quelque chose que vous ne connaissez pas, le chaînage des animations UIView ou sa réinitialisation dans le bloc d'achèvement est parfaitement acceptable. En fait, c'est l'un des principaux objectifs du bloc d'achèvement.

Alors voilà. Trois façons différentes d'inverser une animation et de conserver la valeur d'origine. Prendre plaisir!

95
BJ Homer

Voici ma solution. Pour une répétition 2x, animez 1,5x et effectuez vous-même la dernière partie 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 ....

                         }];
                 }];

Pas de clignotement, fonctionne bien.

14
Rudolf Adamkovič

Il existe une propriété repeatCount sur CAMediaTiming. Je pense que vous devez créer un objet CAAnimation explicite et le configurer correctement. Le nombre de répétitions pour la croissance et la décroissance serait de 2, mais vous pouvez le tester.

Quelque chose le long des lignes de cela. Vous auriez cependant besoin de deux objets CAAnimation, un pour le cadre et un pour la rotation.

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 il n'y a aucun moyen d'éviter de programmer deux objets d'animation.

0
GorillaPatch