¿Cómo bloquear confiablemente llamadas de animation duplicadas en Objective-C?

Un usuario puede iniciar una animation con un gesto de deslizar. Quiero bloquear las llamadas duplicadas a la animation, para asegurarme de que una vez que la animation haya comenzado, no se pueda iniciar de nuevo hasta que se haya completado, lo que puede suceder si el usuario desliza accidentalmente varias veces.

Me imagino que la mayoría de las personas logran este control usando una bandera booleana ( BOOL isAnimatingFlag ) de la manera que se muestra abajo. He hecho cosas como esta antes en las aplicaciones muchas veces, pero nunca me siento 100% seguro de si mi bandera está garantizada para tener el valor que pretendo, ya que la animation usa bloques y no me queda claro qué hilo completó mi animation bloque se está ejecutando.

¿Es de esta manera (de locking de animaciones duplicadas) confiable para la ejecución de múltiples hilos?

 /* 'atomic' doesn't * guarantee thread safety * I've set up my flag as follows: * Does this look correct for the intended usage? */ @property (assign, nonatomic) BOOL IsAnimatingFlag; //… @synthesize IsAnimatingFlag //… -(void)startTheAnimation{ // (1) return if IsAnimatingFlag is true if(self.IsAnimatingFlag == YES)return; /* (2) set IsAnimatingFlag to true * my intention is to prevent duplicate animations * which may be caused by an unwanted double-tap */ self.etiIsAnimating = YES; // (3) start a new animation [UIView animateWithDuration:0.75 delay:0.0 options:nil animations:^{ // animations would happen here... } completion:^(BOOL finished) { // (4) reset the flag to enable further animations self.IsAnimatingFlag = NO; }]; } 

Deshabilite el gesto si no desea que el usuario lo active varias veces

 - (void)startTheAnimation:(id)sender { [sender setEnabled:NO]; [UIView animateWithDuration:0.75 delay:0.0 options:nil animations:^{ // animations would happen here... } completion:^(BOOL finished) { [sender setEnabled:YES]; }]; } 

Actualizar

Los gestos también tienen una propiedad enabled para que pueda usar la misma idea como si fuera un button y cambiar su estado habilitado

El bloque de finalización de animation siempre se ejecutará en el hilo principal.

En el ejemplo de Referencia de class UIView , puede ver que [view removeFromSuperview] se llama directamente desde el bloque. Eso significa que un bloque de finalización se ejecuta en el subprocess principal, ya que es el único subprocess seguro para llamar a los methods rellenados con interfaz de usuario.

Por lo que todos son buenos si llama startTheAnimation solo desde el hilo principal. Si no es así, deberá enviarlo en el hilo principal de todos modos porque llamará a los methods rellenados con UI.

Si necesita llamar a startTheAnimation desde otros subprocesss que el subprocess principal, puede hacer algo como esto:

 -(void)startTheAnimation{ dispatch_async(dispatch_get_main_queue(), ^{ // Your code here }); } 

Por supuesto, es mejor desde el punto de vista de la experiencia del usuario, por ejemplo, deshabilitar un button o modificar la interfaz de usuario de otras forms para indicar que una animation está en progreso. Sin embargo, es el mismo código. Lo que sea que necesite hacer primero debe desactivarlo antes de que comience una animation y volver a habilitarla después de que haya finalizado.

Cuando llame a este método, puede intentar usar la function GCD dispatch_once:

 static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ [myThingy startTheAnimation]; });