ios - Annullare un'animazione UIView?


È possibile cancellare un'animazione UIView mentre è in corso? O dovrei scendere al livello di CA?

cioè ho fatto qualcosa di simile (magari impostando anche un'azione di fine animazione):

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

// set view properties

[UIView commitAnimations];

Ma prima che l'animazione sia completata e ottengo l'evento di animazione terminato, voglio cancellarlo (taglia corto). È possibile? Googling trova alcune persone che fanno la stessa domanda senza risposte - e una o due persone speculano che non può essere fatto.




Answers


Il modo in cui lo faccio è quello di creare una nuova animazione per il tuo punto finale. Imposta una durata molto breve e assicurati di utilizzare +setAnimationBeginsFromCurrentState: metodo per iniziare dallo stato corrente. Quando lo si imposta su SÌ, l'animazione corrente viene interrotta. Sembra qualcosa del genere:

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

// set view properties

[UIView commitAnimations];



Uso:

#import <QuartzCore/QuartzCore.h>

.......

[myView.layer removeAllAnimations];



Il modo più semplice per interrompere immediatamente tutte le animazioni su una vista particolare è questo:

Collega il progetto a QuartzCore.framework. All'inizio del tuo codice:

#import <QuartzCore/QuartzCore.h>

Ora, quando vuoi interrompere tutte le animazioni su una vista morta nelle loro tracce, dì questo:

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

La linea di mezzo funzionerebbe da sola, ma c'è un ritardo fino a quando il runloop finisce (il "momento di ridisegno"). Per evitare questo ritardo, avvolgere il comando in un blocco di transazione esplicito come mostrato. Questo funziona a condizione che non siano state apportate altre modifiche a questo livello nell'attuale runloop.




Su iOS 4 e UIViewAnimationOptionBeginFromCurrentState , utilizza l'opzione UIViewAnimationOptionBeginFromCurrentState nella seconda animazione per tagliare la prima animazione breve.

Ad esempio, si supponga di avere una vista con un indicatore di attività. Desiderate attenuare l'indicatore di attività mentre inizia un'attività potenzialmente dispendiosa in termini di tempo e svanirla quando l'attività è terminata. Nel codice qui sotto, la vista con l'indicatore di attività su di esso si chiama 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;
                     }
                 }];
}



Per cancellare un'animazione è sufficiente impostare la proprietà attualmente animata, al di fuori dell'animazione UIView. Questo fermerà l'animazione dovunque sia, e l'UIView salterà all'impostazione appena definita.




Se si sta animando un vincolo cambiando la costante anziché una proprietà della vista, nessuno degli altri metodi funziona su iOS 8.

Esempio di animazione:

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) {

                 }];

Soluzione:

È necessario rimuovere le animazioni dai livelli di qualsiasi vista influenzata dalla modifica del vincolo e dai relativi sottolivelli.

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






Nessuno dei precedenti ha risolto il problema per me, ma ciò ha aiutato: l'animazione UIView imposta immediatamente la proprietà, quindi la anima. Arresta l'animazione quando il livello di presentazione corrisponde al modello (la proprietà set).

Ho risolto il mio problema, che era "Voglio animare da dove sembri che appari" ("tu" che significa la vista). Se lo vuoi, allora:

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

imposta la posizione sul livello di presentazione

Uso alcune opzioni tra cui UIViewAnimationOptionOverrideInheritedDuration

Ma poiché la documentazione di Apple è vaga, non so se in realtà sovrascrive le altre animazioni quando viene utilizzata, o semplicemente reimposta i timer.

[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) {}];

E questa dovrebbe essere una soluzione decente per ora.




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



Ho lo stesso problema; le API non hanno nulla per cancellare alcune animazioni specifiche. Il

+ (void)setAnimationsEnabled:(BOOL)enabled

disabilita TUTTE le animazioni e quindi non funziona per me. Ci sono due soluzioni:

1) rendere il tuo oggetto animato una sottoview. Quindi, quando si desidera annullare le animazioni per quella vista, rimuovere la vista o nasconderla. Molto semplice, ma è necessario ricreare la sottoview senza animazioni se è necessario tenerla in vista.

2) ripeti l'anim solo uno, e fai un selettore dei delegati per riavviare l'anim se necessario, come questo:

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

Problema con 2) è che c'è sempre un ritardo nell'arresto dell'anim nel momento in cui termina il ciclo di animazione corrente.

Spero che questo ti aiuti.




Ci scusiamo per resuscitare questa risposta, ma in iOS 10 le cose sono cambiate, e ora l'annullamento è possibile e puoi persino annullare con grazia!

Dopo iOS 10 puoi annullare le animazioni con UIViewPropertyAnimator !

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

Se si passa true, annulla l'animazione e si ferma proprio dove l'hai annullata. Il metodo di completamento non verrà chiamato. Tuttavia, se passi falso, sei responsabile della fine dell'animazione:

animator.finishAnimation(.start)

Puoi terminare la tua animazione e rimanere nello stato corrente (.corrente) o andare allo stato iniziale (.start) o allo stato finale (.end)

A proposito, puoi anche mettere in pausa e ricominciare più tardi ...

animator.pauseAnimation()
animator.startAnimation()

Nota: se non si desidera annullare bruscamente, è possibile invertire l'animazione o addirittura modificare l'animazione dopo averla messa in pausa!




Anche se annulli l'animazione nei modi sopra l'animazione, didStopSelector ancora in esecuzione. Quindi, se nella tua applicazione ci sono stati logici guidati da animazioni, avrai dei problemi. Per questo motivo, con le modalità sopra descritte, utilizzo la variabile di contesto delle animazioni di UIView . Se si passa lo stato corrente del programma dal parametro contesto all'animazione, quando l'animazione si interrompe, la funzione didStopSelector può decidere se fare qualcosa o semplicemente tornare in base allo stato corrente e il valore dello stato passato come contesto.




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



Per mettere in pausa un'animazione senza ripristinare lo stato originale o finale:

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