web-dev-qa-db-fra.com

Annuler une animation UIView?

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

c’est-à-dire que j’ai fait quelque chose comme ceci (peut-être en définissant une action de fin d’animation également):

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

// set view properties

[UIView commitAnimations];

Mais avant que l'animation ne se termine et que je reçoive l'événement terminé par l'animation, je souhaite l'annuler (couper la parole). Est-ce possible? Googling autour trouve quelques personnes posant la même question sans réponse - et une ou deux personnes spéculant que cela ne peut pas être fait.

230
philsquared

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 d'utiliser la méthode +setAnimationBeginsFromCurrentState: pour démarrer à partir de l'état actuel. Lorsque vous définissez cette option sur OUI, l'animation en cours est raccourcie. Ressemble à ceci:

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

// set view properties

[UIView commitAnimations];
113

Utilisation:

#import <QuartzCore/QuartzCore.h>

.......

[myView.layer removeAllAnimations];
350
Jim Heising

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

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

#import <QuartzCore/QuartzCore.h>

Maintenant, quand vous voulez arrêter toutes les animations d'une vue morte, 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 cycle (le "redessin moment") Pour éviter ce délai, encapsulez la commande dans un bloc de transaction explicite, comme indiqué. Cela fonctionne à condition qu'aucune autre modification n'ait été effectuée sur cette couche dans le cycle d'exécution actuel.

62
matt

Sur iOS 4 et versions supérieures, utilisez l'option UIViewAnimationOptionBeginFromCurrentStatesur la deuxième animation pour couper la première animation.

Par exemple, supposons que vous ayez une vue avec un indicateur d'activité. Vous souhaitez insérer l’indicateur d’activité au début d’une activité potentiellement longue, puis l’éteindre à la fin de l’activité. 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;
                     }
                 }];
}
45
Ville Laurikari

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ù qu'elle soit et UIView passera au paramètre que vous venez de définir.

40
tomtaylor

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 l’annuler gracieusement!

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

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

Si vous passez vrai, l'animation est annulée et elle 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 false, vous êtes responsable de terminer l'animation:

animator.finishAnimation(.start)

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

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

animator.pauseAnimation()
animator.startAnimation()

Remarque: Si vous ne souhaitez pas d'annuler brutalement, vous pouvez inverser votre animation ou même la modifier après l'avoir suspendue!

16
Tiago Almeida

Si vous animez une contrainte en modifiant la constante au lieu d'une propriété de vue, 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 de toutes les 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];
}
9
Nikola Lajic

Si vous voulez juste mettre en pause/arrêter l'animation en douceur

self.yourView.layer.speed = 0;

Source: Comment mettre en pause l'animation d'un arbre de calque

8
Victor M

Rien de ce qui précède ne l'a résolu pour moi, mais cela 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 à partir de l'endroit où vous semblez apparaître" ("vous" signifie la vue). Si vous voulez que, alors:

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

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

J'utilise quelques options dont UIViewAnimationOptionOverrideInheritedDuration

Mais comme la documentation d’Apple est vague, je ne sais pas si elle remplace réellement les autres animations lorsqu’elle est utilisée ou si elle réinitialise simplement les minuteries. 

[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 le moment.

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

Version rapide de la solution de Stephen Darlington

UIView.beginAnimations(nil, context: nil)
UIView.setAnimationBeginsFromCurrentState(true)
UIView.setAnimationDuration(0.1)
// other animation properties

// set view properties
UIView.commitAnimations()
1
lucamegh

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) faites de votre objet animé une sous-vue. Ensuite, lorsque vous souhaitez annuler les animations pour cette vue, supprimez-la ou masquez-la. Très simple, mais vous devez recréer la sous-vue sans animations si vous devez la garder en vue.

2) répétez l’animation une seule et faites un sélecteur de délégués pour redémarrer l’animation 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 ya toujours du retard à arrêter l’animation à la fin du cycle d’animation en cours.

J'espère que cela t'aides.

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

Pour mettre une animation en pause sans rétablir l'état d'origine ou final, procédez comme suit:

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

Même si vous annulez l'animation de la manière indiquée ci-dessus, l'animation didStopSelector est toujours exécutée. Donc, si vous avez des états logiques dans votre application pilotés par des animations, vous aurez des problèmes. Pour cette raison, avec les moyens décrits ci-dessus, j'utilise la variable de contexte de UIView animations. Si vous transmettez à l'animation l'état actuel de votre programme par le paramètre context, l'animation arrête votre fonction didStopSelector et peut décider si elle doit faire quelque chose ou simplement renvoyer en fonction de l'état actuel et de la valeur d'état transmise comme contexte.

0
Gorky

Aucune des solutions répondues n'a fonctionné pour moi. J'ai résolu mes problèmes de cette façon (je ne sais pas si c'est correct?), Car j'ai eu des problèmes en appelant cela trop vite (alors que l'animation précédente n'était pas encore terminée). Je passe mon animation voulue avec le bloc customAnim.

extension UIView
{

    func niceCustomTranstion(
        duration: CGFloat = 0.3,
        options: UIView.AnimationOptions = .transitionCrossDissolve,
        customAnim: @escaping () -> Void
        )
    {
        UIView.transition(
            with: self,
            duration: TimeInterval(duration),
            options: options,
            animations: {
                customAnim()
        },
            completion: { (finished) in
                if !finished
                {
                    // NOTE: This fixes possible flickering ON FAST TAPPINGS
                    // NOTE: This fixes possible flickering ON FAST TAPPINGS
                    // NOTE: This fixes possible flickering ON FAST TAPPINGS
                    self.layer.removeAllAnimations()
                    customAnim()
                }
        })

    }

}
0
sabiland

Lorsque je travaille avec une animation UIStackView, outre removeAllAnimations(), je dois définir certaines valeurs sur une valeur initiale, car removeAllAnimations() peut les définir sur un état imprévisible. J'ai stackView avec view1 et view2 à l'intérieur, et une vue doit être visible et une cachée:

public func configureStackView(hideView1: Bool, hideView2: Bool) {
    let oldHideView1 = view1.isHidden
    let oldHideView2 = view2.isHidden
    view1.layer.removeAllAnimations()
    view2.layer.removeAllAnimations()
    view.layer.removeAllAnimations()
    stackView.layer.removeAllAnimations()
    // after stopping animation the values are unpredictable, so set values to old
    view1.isHidden = oldHideView1 //    <- Solution is here
    view2.isHidden = oldHideView2 //    <- Solution is here

    UIView.animate(withDuration: 0.3,
                   delay: 0.0,
                   usingSpringWithDamping: 0.9,
                   initialSpringVelocity: 1,
                   options: [],
                   animations: {
                    view1.isHidden = hideView1
                    view2.isHidden = hideView2
                    stackView.layoutIfNeeded()
    },
                   completion: nil)
}
0
Paul T.