UIButton no se puede tocar mientras está animado con UIView animateWithDuration

Tengo el siguiente código:

[UIView animateWithDuration:0.3 delay:0.0 options:UIViewAnimationCurveEaseOut | UIViewAnimationOptionAllowUserInteraction animations:^{ CGRect r = [btn frame]; r.origin.y -= 40; [btn setFrame: r]; } completion:^(BOOL done){ if(done){ [UIView animateWithDuration:0.3 delay:1 options:UIViewAnimationOptionCurveEaseIn | UIViewAnimationOptionAllowUserInteraction animations:^{ CGRect r = [btn frame]; r.origin.y += 40; [btn setFrame: r]; } completion:^(BOOL done){if(done) zombiePopping = 0; }]; } }]; 

El problema es que parece que el botón no responde a los toques mientras se está UIViewAnimationOptionAllowInteraction aunque estoy usando UIViewAnimationOptionAllowInteraction , lo cual es un poco extraño para mí.

¿Quizás esto se haga con Core Animation para que funcione? y si es así, ¿cómo lo haré?

La parte táctil del botón no coincidirá con el marco visible del botón cuando se está animando.

Internamente, el marco del botón se establecerá en el valor final de su animación. Debería encontrar que puede tocar en esta área, y funcionaría.

Para presionar un botón en movimiento, debe realizar una prueba de impacto en la propiedad .layer.presentationLayer del botón (necesita importar el marco QuartzCore para hacer esto). Esto normalmente se haría en los métodos de manejo táctil en su controlador de vista.

Me complace ampliar esta respuesta si necesita más.

Así es como respondería a un evento táctil al golpear las capas de presentación de prueba. Este código está en la subclase del controlador de vista que administra sus botones. Cuando hice esto, me interesó el toque inicial en lugar del toque final (que normalmente es cuando se registraría un toque) pero el principio es el mismo.

Recuerde importar el marco QuartzCore y agréguelo a su proyecto.

 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { UITouch *touch = [touches anyObject]; CGPoint touchLocation = [touch locationInView:self.view]; for (UIButton *button in self.buttonsOutletCollection) { if ([button.layer.presentationLayer hitTest:touchLocation]) { // This button was hit whilst moving - do something with it here break; } } } 

En mi caso, configuro superView.alpha = 0 , detiene la interacción del botón aunque configuro UIViewAnimationOptionAllowUserInteraction .

¿Solución? Fácil, solo reduce alpha a 0.1 en vez de 0

 [UIView animateWithDuration:durationTime delay:0 options:UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionOverrideInheritedOptions | UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionRepeat | UIViewAnimationOptionAutoreverse animations: ^{ superView.alpha = 0.1; // 0.0 will make the button unavailable to touch } completion:nil]; 

El orden de las opciones es importante, primero UIViewAnimationOptionAllowUserInteraction colocar UIViewAnimationOptionAllowUserInteraction y luego agregar otras opciones.

En Swift 3 use: .allowUserInteraction

Lo he logrado usando NSTimer: Primero, ejecute la animación de inicio, luego debe iniciar un temporizador, este es el código de demostración:

 -(void)fun··· { [UIView animateWithDuration:0.3 delay:0.0 options:UIViewAnimationCurveEaseOut | UIViewAnimationOptionAllowUserInteraction animations:^{ CGRect r = [btn frame]; r.origin.y -= 40; [btn setFrame: r]; } completion:^(BOOL done){}]; then , you should start an timer,example: //start timer if (timer != nil) { [timer invalidate]; timer = nil; } [self performSelector:@selector(closeButton) withObject:nil afterDelay:0]; } - (void)closeButton { if (timer != nil) { return; } //start timer timer = [NSTimer scheduledTimerWithTimeInterval:1.5f target:self selector:@selector(timerEvent) userInfo:nil repeats:NO]; [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes]; } - (void)timerEvent { [UIView animateWithDuration:0.3 delay:1 options:UIViewAnimationOptionCurveEaseIn | UIViewAnimationOptionAllowUserInteraction animations:^{ CGRect r = [btn frame]; r.origin.y += 40; [btn setFrame: r]; } completion:^(BOOL done){if(done) zombiePopping = 0; }]; [timer invalidate]; timer = nil; } 

La misma respuesta en Swift (2.0), para los perezosos. Reemplazar con una colección de bucle y salida si es necesario:

 override func touchesBegan(touches: Set, withEvent event: UIEvent?) { let touch = touches.first let touchLocation = touch!.locationInView(self.view) if self.button.layer.presentationLayer()!.hitTest(touchLocation) != nil { action() // Your action, preferably the original button action. } } 

Swift 3.0 Crea una supervista en la parte superior en viewdidload

 let superView = UIView(frame: view.frame) superView.isUserInteractionEnabled = true superView.backgroundColor = UIColor.clear superView.alpha = 0.1 view.addSubview(superView) 

Ahora implementa touchesbagan como este

 override func touchesBegan(_ touches: Set, with event: UIEvent?) { guard let touch = (touches as NSSet).anyObject() as? UITouch else { return } let touchLocation = touch.location(in: self.view) if btnone.layer.presentation()?.hitTest(touchLocation) != nil { print("1") // Do stuff for button } if btntwo.layer.presentation()?.hitTest(touchLocation) != nil { print("2") // Do stuff } if btnthree.layer.presentation()?.hitTest(touchLocation) != nil { print(3) // Do stuff } if btnfour.layer.presentation()?.hitTest(touchLocation) != nil { print(4) // Do stuff } } 

Versión Swift: ‘999’ es el valor de etiqueta utilizado para identificar la vista de destino.

 override func touchesBegan(touches: Set, withEvent event: UIEvent?) { let touch = touches.first let touchLocation = (touch?.locationInView(self.rootView))! for view in self.view.subviews { if let layer = view.layer.presentationLayer()?.hitTest(touchLocation) where view.tag == 999 { // Here view is the tapped view print(view) } } } 

Funciona de maravilla

override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? { if bounds.contains(point) && !hidden && userInteractionEnabled && enabled { return self } return super.hitTest(point, withEvent: event) }

Aunque esta respuesta también es correcta