¿Qué pasa con el tamaño del área táctil de arrastrar / salir de UIButton?

Bueno, creo que es mejor mostrar lo que quiero decir:

Animación de un UIButton reaccionando a movimientos de arrastre

Puede ver claramente que una vez que hemos tocado el button y salido de él, un evento de movimiento consecuente desencadena el cambio de estado del button desde muy lejos.

Si bien este comportamiento es natural para todos los botones UIB, no pude search una solución para modificarlo.

¿Hay alguna manera de networkingucir el área de golpe para este tipo de sensibilidad de UIButton? Quiero que se networkinguzca, porque siento que el button es lo suficientemente grande como es, y proporcionará una mejor experiencia de usuario junto con los efectos de sonido ascendente / descendente.

UPD: el siguiente código de anulación para UIButton se publicó en otro hilo :

- (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event { CGFloat boundsExtension = 25.0f; CGRect outerBounds = CGRectInset(self.bounds, -1 * boundsExtension, -1 * boundsExtension); BOOL touchOutside = !CGRectContainsPoint(outerBounds, [touch locationInView:self]); if(touchOutside) { BOOL previousTouchInside = CGRectContainsPoint(outerBounds, [touch previousLocationInView:self]); if(previousTouchInside) { NSLog(@"Sending UIControlEventTouchDragExit"); [self sendActionsForControlEvents:UIControlEventTouchDragExit]; } else { NSLog(@"Sending UIControlEventTouchDragOutside"); [self sendActionsForControlEvents:UIControlEventTouchDragOutside]; } } return [super continueTrackingWithTouch:touch withEvent:event]; } 

Altera la extensión de área de hit utilizada por los events de Arrastre / Desplácese, pero los estados del button Arriba / Abajo cambian exactamente de la misma manera que lo hicieron antes.

No sé si todavía está teniendo el mismo problema, pero pude solucionarlo usando un código similar en el método touchesEnded: withEvent:.

También cambié ese método para agregar touchEnter y dragInside, ya que con el código actual, los events aún usaban los mismos límites. Además, hice que cada uno de los casos devuelva SÍ para que no se llame al súper (causaría que el toque arrastre dentro se llame prematuramente).

Aquí está el código final con el que terminé, en dos methods:

 - (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event { CGFloat boundsExtension = 25.0f; CGRect outerBounds = CGRectInset(self.bounds, -1 * boundsExtension, -1 * boundsExtension); BOOL touchOutside = !CGRectContainsPoint(outerBounds, [touch locationInView:self]); if(touchOutside) { BOOL previousTouchInside = CGRectContainsPoint(outerBounds, [touch previousLocationInView:self]); if(previousTouchInside) { [self sendActionsForControlEvents:UIControlEventTouchDragExit]; return YES; } else { [self sendActionsForControlEvents:UIControlEventTouchDragOutside]; return YES; } } else { BOOL previousTouchOutside = !CGRectContainsPoint(outerBounds, [touch previousLocationInView:self]); if (previousTouchOutside) { [self sendActionsForControlEvents:UIControlEventTouchDragEnter]; return YES; } else { [self sendActionsForControlEvents:UIControlEventTouchDragInside]; return YES; } } return [super continueTrackingWithTouch:touch withEvent:event]; } - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { UITouch *touch = [touches anyObject]; CGFloat boundsExtension = 25.0f; CGRect outerBounds = CGRectInset(self.bounds, -1 * boundsExtension, -1 * boundsExtension); BOOL touchInside = CGRectContainsPoint(outerBounds, [touch locationInView:self]); if (touchInside) { return [self sendActionsForControlEvents:UIControlEventTouchUpInside]; } else { return [self sendActionsForControlEvents:UIControlEventTouchUpOutside]; } return [super endTrackingWithTouch:touch withEvent:event]; } 

NOTA: No es necesario devolver el súper del método al final, pero lo dejé ahí para que esté completo.

Una versión Swift:

  private let _boundsExtension: CGFloat = 0 // Adjust this as needed override func continueTrackingWithTouch(touch: UITouch, withEvent event: UIEvent?) -> Bool { let outerBounds: CGRect = CGRectInset(bounds, -1 * _boundsExtension, -1 * _boundsExtension) let currentLocation: CGPoint = touch.locationInView(self) let previousLocation: CGPoint = touch.previousLocationInView(self) let touchOutside: Bool = !CGRectContainsPoint(outerBounds, currentLocation) if touchOutside { let previousTouchInside: Bool = CGRectContainsPoint(outerBounds, previousLocation) if previousTouchInside { sendActionsForControlEvents(.TouchDragExit) } else { sendActionsForControlEvents(.TouchDragOutside) } } else { let previousTouchOutside: Bool = !CGRectContainsPoint(outerBounds, previousLocation) if previousTouchOutside { sendActionsForControlEvents(.TouchDragEnter) } else { sendActionsForControlEvents(.TouchDragInside) } } return true } override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) { let touch: UITouch = touches.first! let outerBounds: CGRect = CGRectInset(bounds, -1 * _boundsExtension, -1 * _boundsExtension) let currentLocation: CGPoint = touch.locationInView(self) let touchInside: Bool = CGRectContainsPoint(outerBounds, currentLocation) if touchInside { return sendActionsForControlEvents(.TouchUpInside) } else { return sendActionsForControlEvents(.TouchUpOutside) } }