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?
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:
CGPoint oldCenter = someView.center;
[UIView animateWithDuration:2.0
animations: ^{ someView.center = newPoint; }
completion:
^(BOOL finished) {
[UIView animateWithDuration:2.0
animations:^{ someView.center = oldCenter; }];
}];
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.
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!
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.
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.