¿Cómo crear un CAAnimation retrasado que se puede cancelar y / o revertir durante la reproducción?

Tengo una capa que quiero mostrar cuando el usuario hace una acción, y me escondo después de que termine. La capa se muestra nuevamente si el usuario vuelve a hacer la acción.

Para que la UI no se vuelva loca con las animaciones, quiero:

  • que la animation de fadeout solo comienza después de un segundo (y se cancela si el usuario hace la acción antes de que haya comenzado)
  • la animation de fadein para mostrar la capa comienza con la opacidad de fadeout actual si el usuario está haciendo acción antes de que la capa haya desaparecido

¿Cuál es la mejor manera de hacer esto?

Probé esto, pero esto no funcionará correctamente (gran cantidad de ruido con la capa parpadeando):

- (void)hideHintLayer:(bool)hide { if(hide) { CABasicAnimation *animation = [CABasicAnimation animation]; animation.beginTime = CACurrentMediaTime() + 1.0f; animation.duration = 1.0f; animation.fillMode = kCAFillModeForwards; animation.removedOnCompletion = NO; animation.keyPath = @"opacity"; animation.fromValue = @(1.0f); animation.toValue = @(0.0f); [layer addAnimation:animation forKey:nil]; } else { layer.opacity = 1.0f; } } 

Si quieres detener una animation, solo puedes hacer

 [layer removeAllAnimations]; 

Si desea conocer el alpha actual durante la ocultación animada de la vista (para que pueda invertir la animation, comenzando desde el lugar correcto, puede hacer lo siguiente:

 CALayer *presentationLayer = layer.presentationLayer; CGFloat startingAlpha = presentationLayer.opacity; 

A continuación, puede configurar el alfa para que pase de startingAlpha a 1.0 para animar el mostrar sin que parpadee la pantalla.

Puede hacer las animaciones reales usando animation basada en bloques, o supongo que podría usar CABasicAnimation , aunque no estoy seguro de por qué lo haría.


Entonces, por ejemplo, podrías hacer algo como lo siguiente (en mi ejemplo, tengo un button "mostrar"). Estoy usando animaciones en bloque, pero sospecho que también funcionaría bien para CABasicAnimation :

 - (IBAction)onPressShowButton:(id)sender { [self showAndScheduleHide]; } - (void)showAndScheduleHide { [UIView animateWithDuration:1.0 animations:^{ self.containerView.alpha = 1.0; } completion:^(BOOL finished) { [self scheduleHide]; }]; } - (void)show { [UIView animateWithDuration:1.0 animations:^{ self.containerView.alpha = 1.0; } completion:nil]; } - (void)scheduleHide { self.timer = [NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:@selector(startToHide) userInfo:nil repeats:NO]; } - (void)startToHide { self.timer = nil; self.hiding = YES; [UIView animateWithDuration:5.0 delay:0.0 options:UIViewAnimationOptionAllowUserInteraction animations:^{ self.containerView.alpha = 0.0; } completion:^(BOOL finished) { self.hiding = NO; }]; } 

A continuación, puede tener algún método de utilidad para revertirlo o reprogtwigr un escondite en progreso:

 - (void)reverseAndPauseHide { // if we have a "hide" scheduled, then cancel that if (self.timer) { [self.timer invalidate]; self.timer = nil; } // if we have a hide in progress, then reverse it if (self.hiding) { [self.containerView.layer removeAllAnimations]; CALayer *layer = self.containerView.layer.presentationLayer; CGFloat currentAlpha = layer.opacity; self.containerView.alpha = currentAlpha; [self show]; } } 

Entonces, la pregunta es cuándo sabe llamar a este reverseAndPauseHide y cuándo volver a scheduleHide . Entonces, por ejemplo, puedes manejar toques:

 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { [super touchesBegan:touches withEvent:event]; [self reverseAndPauseHide]; } - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { [super touchesEnded:touches withEvent:event]; [self scheduleHide]; }