constraint animations animate ios animation uiview uiviewanimation

ios - animate - swift animations



¿Cómo detener y revertir una animación de UIView? (3)

He animado un UIView para que se encoja cuando el usuario toca un botón de alternar y se expande a su tamaño original cuando el usuario toca el botón nuevamente. Hasta ahora todo funciona bien. El problema es que la animación lleva algo de tiempo, por ejemplo, 3 segundos. Durante ese tiempo, aún quiero que el usuario pueda interactuar con la interfaz. Por lo tanto, cuando el usuario toca nuevamente el botón mientras la animación todavía está en progreso, se supone que la animación se detiene justo donde está y se invierte.

En las preguntas y respuestas de Apple, he encontrado una forma de pausar todas las animaciones inmediatamente:

https://developer.apple.com/library/ios/#qa/qa2009/qa1673.html

Pero no veo una forma de invertir la animación desde aquí (y omitir el resto de la animación inicial). ¿Cómo logro esto?

- (IBAction)toggleMeter:(id)sender { if (self.myView.hidden) { self.myView.hidden = NO; [UIView animateWithDuration:3 animations:^{ self.myView.transform = expandMatrix; } completion:nil]; } else { [UIView animateWithDuration:3 animations:^{ self.myView.transform = shrinkMatrix; } completion:^(BOOL finished) { self.myView.hidden = YES; }]; } }


Hay un truco común que puede usar para hacer esto, pero es necesario escribir un método separado para reducir (y otro similar para expandir):

- (void) shrink { [UIView animateWithDuration:0.3 animations:^{ self.myView.transform = shrinkALittleBitMatrix; } completion:^(BOOL finished){ if (continueShrinking && size>0) { size=size-1; [self shrink]; } }]; }

Entonces, el truco es romper la animación de 3 segundos de encoger en 10 animaciones (o más de 10, por supuesto) de 0.3 segundos cada una en las que shrinkALittleBitMatrix 1/10 de toda la animación: shrinkALittleBitMatrix . Después de finalizar cada animación, se llama al mismo método solo cuando el booleador continueShrinking encogimiento es verdadero y cuando el size entero es positivo (la vista en tamaño completo sería tamaño = 10 y la vista con tamaño mínimo sería tamaño = 0). Cuando presiona el botón, cambia el ivar continueShrinking a FALSE, y luego se expand . Esto detendrá la animación en menos de 0.3 segundos.

Bueno, tienes que completar los detalles, pero espero que ayude.


Además de lo siguiente (en el cual tomamos el estado actual de la capa de presentación, dejamos de hacer la animación, reseteamos el estado actual de la capa de presentación guardada e iniciamos la nueva animación), hay una solución mucho más fácil.

Si realiza animaciones basadas en bloques, si desea detener una animación e iniciar una nueva animación en versiones de iOS anteriores a la 8.0, simplemente puede usar la opción UIViewAnimationOptionBeginFromCurrentState . (Efectivo en iOS 8, el comportamiento predeterminado no es solo comenzar desde el estado actual, sino hacerlo de una manera que refleje tanto la ubicación actual como la velocidad actual, haciendo que en gran medida sea innecesario preocuparse por este problema en absoluto . Consulte el video WWDC 2014 Creación de interacciones intercaladas y receptivas para obtener más información.)

[UIView animateWithDuration:3.0 delay:0.0 options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionAllowUserInteraction animations:^{ // specify the new `frame`, `transform`, etc. here } completion:NULL];

Puede lograr esto al detener la animación actual y comenzar la nueva animación desde donde la dejó. Puedes hacer esto con Quartz 2D :

  1. Agregue QuartzCore.framework a su proyecto si aún no lo ha hecho. (En versiones contemporáneas de Xcode, a menudo es innecesario hacer esto explícitamente ya que se vincula automáticamente al proyecto).

  2. Importe el encabezado necesario si aún no lo ha hecho (una vez más, no es necesario en las versiones actuales de Xcode):

    #import <QuartzCore/QuartzCore.h>

  3. Haga que su código detenga la animación existente:

    [self.subview.layer removeAllAnimations];

  4. Obtenga una referencia a la capa de presentación actual (es decir, el estado de la vista, ya que es precisamente en este momento):

    CALayer *currentLayer = self.subview.layer.presentationLayer;

  5. Restablezca la transform (o frame o lo que sea) de acuerdo con el valor actual en el presentationLayer :

    self.subview.layer.transform = currentLayer.transform;

  6. Ahora anima desde esa transform (o frame o lo que sea) al nuevo valor:

    [UIView animateWithDuration:1.0 delay:0.0 options:UIViewAnimationOptionAllowUserInteraction animations:^{ self.subview.layer.transform = newTransform; } completion:NULL];

Poniendo todo esto en orden, aquí hay una rutina que alterna mi escala de transformación de 2.0x para identificar y retroceder:

- (IBAction)didTouchUpInsideAnimateButton:(id)sender { CALayer *currentLayer = self.subview.layer.presentationLayer; [self.subview.layer removeAllAnimations]; self.subview.layer.transform = currentLayer.transform; CATransform3D newTransform; self.large = !self.large; if (self.large) newTransform = CATransform3DMakeScale(2.0, 2.0, 1.0); else newTransform = CATransform3DIdentity; [UIView animateWithDuration:1.0 delay:0.0 options:UIViewAnimationOptionAllowUserInteraction animations:^{ self.subview.layer.transform = newTransform; } completion:NULL]; }

O si desea cambiar los tamaños de frame de 100x100 a 200x200 y viceversa:

- (IBAction)didTouchUpInsideAnimateButton:(id)sender { CALayer *currentLayer = self.subview.layer.presentationLayer; [self.subview.layer removeAllAnimations]; CGRect newFrame = currentLayer.frame; self.subview.frame = currentLayer.frame; self.large = !self.large; if (self.large) newFrame.size = CGSizeMake(200.0, 200.0); else newFrame.size = CGSizeMake(100.0, 100.0); [UIView animateWithDuration:1.0 delay:0.0 options:UIViewAnimationOptionAllowUserInteraction animations:^{ self.subview.frame = newFrame; } completion:NULL]; }

Por cierto, aunque en realidad no importa para animaciones realmente rápidas, para animaciones lentas como la tuya, es posible que desees establecer la duración de la animación de inversión para que coincida con lo avanzado que has progresado en tu animación actual ( por ejemplo, si estás 0.5 segundos en una animación de 3.0 segundos, cuando retrocedes, probablemente no quieras tomar 3.0 segundos para revertir esa pequeña porción de la animación que has hecho hasta ahora, sino solo 0.5 segundos). Por lo tanto, eso podría verse así:

- (IBAction)didTouchUpInsideAnimateButton:(id)sender { CFTimeInterval duration = kAnimationDuration; // default the duration to some constant CFTimeInterval currentMediaTime = CACurrentMediaTime(); // get the current media time static CFTimeInterval lastAnimationStart = 0.0; // media time of last animation (zero the first time) // if we previously animated, then calculate how far along in the previous animation we were // and we''ll use that for the duration of the reversing animation; if larger than // kAnimationDuration that means the prior animation was done, so we''ll just use // kAnimationDuration for the length of this animation if (lastAnimationStart) duration = MIN(kAnimationDuration, (currentMediaTime - lastAnimationStart)); // save our media time for future reference (i.e. future invocations of this routine) lastAnimationStart = currentMediaTime; // if you want the animations to stay relative the same speed if reversing an ongoing // reversal, you can backdate the lastAnimationStart to what the lastAnimationStart // would have been if it was a full animation; if you don''t do this, if you repeatedly // reverse a reversal that is still in progress, they''ll incrementally speed up. if (duration < kAnimationDuration) lastAnimationStart -= (kAnimationDuration - duration); // grab the state of the layer as it is right now CALayer *currentLayer = self.subview.layer.presentationLayer; // cancel any animations in progress [self.subview.layer removeAllAnimations]; // set the transform to be as it is now, possibly in the middle of an animation self.subview.layer.transform = currentLayer.transform; // toggle our flag as to whether we''re looking at large view or not self.large = !self.large; // set the transform based upon the state of the `large` boolean CATransform3D newTransform; if (self.large) newTransform = CATransform3DMakeScale(2.0, 2.0, 1.0); else newTransform = CATransform3DIdentity; // now animate to our new setting [UIView animateWithDuration:duration delay:0.0 options:UIViewAnimationOptionAllowUserInteraction animations:^{ self.subview.layer.transform = newTransform; } completion:NULL]; }


Primero : ¿cómo eliminar o cancelar una animación con vista?

[view.layer removeAllAnimations]

si la vista tiene muchas animaciones, por ejemplo, una animación se mueve de arriba a abajo, la otra se mueve de izquierda a derecha;

puede cancelar o eliminar una animación especial como esta:

[view.layer removeAnimationForKey:@"someKey"]; // the key is you assign when you create a animation CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"someKey"];

cuando lo haces, la animación se detendrá, invocará a su delegado:

- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag

if flag == 1, indica que la animación se completó. si la bandera == 0, indica que la animación no se completó, puede ser cancelada, eliminada.

Segundo: entonces, puedes hacer lo que quieras hacer en este método delegado.

si desea obtener el marco de la vista cuando se ejecuta el código de eliminación, puede hacer esto:

currentFrame = view.layer.presentationlayer.frame;

Nota:

Cuando obtiene el fotograma actual y elimina la animación, la vista también animará un período de tiempo, por lo que currentFrame no es el último fotograma en la pantalla del dispositivo.

No puedo resolver esta pregunta en este momento. si algún día puedo, actualizaré esta pregunta.