ios - Annuler une animation UIView?


Est-il possible d'annuler une animation UIView en cours? Ou devrais-je passer au niveau CA?

c'est-à-dire que j'ai fait quelque chose comme ça (peut-être mettre une animation de fin aussi):

[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:duration];
[UIView setAnimationCurve: UIViewAnimationCurveLinear];
// other animation properties

// set view properties

[UIView commitAnimations];

Mais avant que l'animation se termine et que j'obtienne l'animation terminée, je veux l'annuler (couper court). Est-ce possible? Googling autour trouve quelques personnes qui posent la même question sans réponses - et une ou deux personnes spéculant que cela ne peut pas être fait.


Answers



La façon dont je le fais est de créer une nouvelle animation à votre point final. Définissez une durée très courte et assurez-vous que vous utilisez la méthode +setAnimationBeginsFromCurrentState: pour démarrer à partir de l'état actuel. Lorsque vous le définissez sur YES, l'animation en cours est coupée. On dirait quelque chose comme ça:

[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:0.1];
[UIView setAnimationCurve: UIViewAnimationCurveLinear];
// other animation properties

// set view properties

[UIView commitAnimations];



Utilisation:

#import <QuartzCore/QuartzCore.h>

.......

[myView.layer removeAllAnimations];



Le moyen le plus simple d'arrêter immédiatement toutes les animations sur une vue particulière est le suivant:

Liez le projet à QuartzCore.framework. Au début de votre code:

#import <QuartzCore/QuartzCore.h>

Maintenant, quand vous voulez arrêter toutes les animations sur une vue morte dans leurs pistes, dites ceci:

[CATransaction begin];
[theView.layer removeAllAnimations];
[CATransaction commit];

La ligne médiane fonctionnerait toute seule, mais il y a un délai jusqu'à la fin du runloop (le "moment de redessiner"). Pour éviter ce délai, enveloppez la commande dans un bloc de transaction explicite comme indiqué. Ce travail à condition qu'aucun autre changement n'ait été effectué sur cette couche dans le runloop actuel.




Sur iOS 4 et supérieur, utilisez l'option UIViewAnimationOptionBeginFromCurrentState sur la deuxième animation pour couper la première animation courte.

À titre d'exemple, supposons que vous ayez une vue avec un indicateur d'activité. Vous souhaitez faire disparaître l'indicateur d'activité pendant le début d'une activité qui prend du temps, et l'éteindre lorsque l'activité est terminée. Dans le code ci-dessous, la vue avec l'indicateur d'activité est appelée activityView .

- (void)showActivityIndicator {
    activityView.alpha = 0.0;
    activityView.hidden = NO;
    [UIView animateWithDuration:0.5
                 animations:^(void) {
                     activityView.alpha = 1.0;
                 }];

- (void)hideActivityIndicator {
    [UIView animateWithDuration:0.5
                 delay:0 options:UIViewAnimationOptionBeginFromCurrentState
                 animations:^(void) {
                     activityView.alpha = 0.0;
                 }
                 completion:^(BOOL completed) {
                     if (completed) {
                         activityView.hidden = YES;
                     }
                 }];
}



Pour annuler une animation, vous devez simplement définir la propriété en cours d'animation, en dehors de l'animation UIView. Cela arrêtera l'animation où que ce soit, et l'UIView passera au réglage que vous venez de définir.




Si vous animez une contrainte en modifiant la constante au lieu d'une propriété view, aucune des autres méthodes ne fonctionne sur iOS 8.

Exemple d'animation:

self.constraint.constant = 0;
[self.view updateConstraintsIfNeeded];
[self.view layoutIfNeeded];
[UIView animateWithDuration:1.0f
                      delay:0.0f
                    options:UIViewAnimationOptionCurveLinear
                 animations:^{
                     self.constraint.constant = 1.0f;
                     [self.view layoutIfNeeded];
                 } completion:^(BOOL finished) {

                 }];

Solution:

Vous devez supprimer les animations des couches des vues affectées par le changement de contrainte et leurs sous-couches.

[self.constraintView.layer removeAllAnimations];
for (CALayer *l in self.constraintView.layer.sublayers)
{
    [l removeAllAnimations];
}






Rien de tout cela ne l'a résolu pour moi, mais cela m'a aidé: L'animation UIView définit la propriété immédiatement, puis l'anime. Il arrête l'animation lorsque la couche de présentation correspond au modèle (la propriété set).

J'ai résolu mon problème, qui était «Je veux animer d'où vous avez l'air d'apparaître» («vous» signifie la vue). Si vous voulez ça, alors:

  1. Ajouter QuartzCore.
  2. CALayer * pLayer = theView.layer.presentationLayer;

définir la position sur la couche de présentation

J'utilise quelques options, y compris UIViewAnimationOptionOverrideInheritedDuration

Mais parce que la documentation d'Apple est vague, je ne sais pas si elle surpasse vraiment les autres animations quand elle est utilisée, ou si elle réinitialise simplement les minuteurs.

[UIView animateWithDuration:blah... 
                    options: UIViewAnimationOptionBeginFromCurrentState ... 
                    animations: ^ {
                                   theView.center = CGPointMake( pLayer.position.x + YOUR_ANIMATION_OFFSET, pLayer.position.y + ANOTHER_ANIMATION_OFFSET);
                   //this only works for translating, but you get the idea if you wanna flip and scale it. 
                   } completion: ^(BOOL complete) {}];

Et cela devrait être une solution décente pour l'instant.




[UIView setAnimationsEnabled:NO];
// your code here
[UIView setAnimationsEnabled:YES];



J'ai le même problème; les API n'ont rien pour annuler une animation spécifique. le

+ (void)setAnimationsEnabled:(BOOL)enabled

désactive toutes les animations, et ne fonctionne donc pas pour moi. Il y a deux solutions:

1) faire de votre objet animé une sous-vue. Ensuite, lorsque vous souhaitez annuler les animations de cette vue, supprimez la vue ou masquez-la. Très simple, mais vous devez recréer la sous-vue sans animations si vous avez besoin de le garder en vue.

2) répétez l'anim seulement un, et faites un sélecteur de délégué pour redémarrer l'anim si nécessaire, comme ceci:

-(void) startAnimation {
NSLog(@"startAnim alpha:%f", self.alpha);
[self setAlpha:1.0];
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:1.0];
[UIView setAnimationRepeatCount:1];
[UIView setAnimationRepeatAutoreverses:YES];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:@selector(pulseAnimationDidStop:finished:context:)];
[self setAlpha:0.1];
[UIView commitAnimations];
}

- (void)pulseAnimationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context {
if(hasFocus) {
    [self startAnimation];
} else {
    self.alpha = 1.0;
}
}

-(void) setHasFocus:(BOOL)_hasFocus {
hasFocus = _hasFocus;
if(hasFocus) {
    [self startAnimation];
}
}

Le problème avec 2) est qu'il y a toujours un délai pour arrêter l'anim qui termine le cycle d'animation en cours.

J'espère que cela t'aides.




Désolé de ressusciter cette réponse, mais dans iOS 10 les choses ont un peu changé, et maintenant l'annulation est possible et vous pouvez même annuler gracieusement!

Après iOS 10, vous pouvez annuler des animations avec UIViewPropertyAnimator !

UIViewPropertyAnimator(duration: 2, dampingRatio: 0.4, animations: {
    view.backgroundColor = .blue
})
animator.stopAnimation(true)

Si vous réussissez, il annule l'animation et s'arrête là où vous l'avez annulée. La méthode d'achèvement ne sera pas appelée. Cependant, si vous passez faux, vous êtes responsable de la fin de l'animation:

animator.finishAnimation(.start)

Vous pouvez terminer votre animation et rester dans l'état actuel (.current) ou passer à l'état initial (.start) ou à l'état final (.end)

En passant, vous pouvez même faire une pause et redémarrer plus tard ...

animator.pauseAnimation()
animator.startAnimation()

Note: Si vous ne voulez pas une annulation brusque, vous pouvez inverser votre animation ou même changer votre animation après l'avoir mise en pause!




Même si vous annulez l'animation dans les manières ci-dessus, l'animation didStopSelector toujours didStopSelector . Donc, si vous avez des états logiques dans votre application entraînés par des animations, vous aurez des problèmes. Pour cette raison, avec les méthodes décrites ci-dessus, j'utilise la variable de UIView animations UIView . Si vous passez l'état actuel de votre programme par le param contextuel à l'animation, lorsque l'animation s'arrête, votre fonction didStopSelector peut décider si elle doit faire quelque chose ou simplement retourner en fonction de l'état actuel et de la valeur d'état passée en contexte.




CALayer * pLayer = self.layer.presentationLayer;
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView animateWithDuration:0.001 animations:^{
    self.frame = pLayer.frame;
}];



Pour suspendre une animation sans revenir à l'état original ou final:

CFTimeInterval paused_time = [myView.layer convertTime:CACurrentMediaTime() fromLayer:nil];
myView.layer.speed = 0.0;
myView.layer.timeOffset = paused_time;