¿Cómo hacer que un botón de intercepción de superview toque eventos?

Digamos que tengo este código:

#import  @interface MyView : UIView @end @implementation MyView - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { // How can I get this to show up even when the button is touched? NSLog(@"%@", [touches anyObject]); } @end @interface TestViewAppDelegate : NSObject  { UIWindow *window; } @end @implementation TestViewAppDelegate - (void)applicationDidFinishLaunching:(UIApplication *)application { window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; MyView *view = [[MyView alloc] initWithFrame:[window frame]]; [view setBackgroundColor:[UIColor whiteColor]]; UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect]; [button setTitle:@"Hiya!" forState:UIControlStateNormal]; [button setFrame:CGRectMake(100.0, 100.0, 200.0, 200.0)]; [view addSubview:button]; [window addSubview:view]; [window makeKeyAndVisible]; } - (void)dealloc { [window release]; [super dealloc]; } @end 

¿Hay alguna manera de interceptar los eventos táctiles que se envían al botón? Lo que me gustaría hacer finalmente es crear una subclase UIView que diga a su controlador de vista (o delegar, lo que sea) cuando detecte un deslizamiento para poder “empujar” el siguiente controlador de vista a la stack (similar a la pantalla de inicio del iPhone ) Pensé que este era el primer paso, pero estoy abierto a sugerencias si me estoy acercando a esto incorrectamente.

Me interesaría ver otras soluciones, pero la forma más fácil que conozco es anular cualquiera de estos dos métodos de UIView:

 - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event; - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event; 

Se llama a estos métodos para determinar si el toque está dentro de los límites de la vista o cualquiera de sus subvistas , por lo que este es un buen punto para interceptar el toque y luego transmitirlo. Simplemente haz lo que quieras, y luego

 return [super hitTest:point withEvent:event]; 

o

 return [super pointInside:point withEvent:event]; 

Instinct diría “subclase UIButton”, pero UIButton es en realidad un clúster de clase (es decir, el tipo real de objeto cambia de forma transparente según el tipo de botón que desee), lo que hace que sea summente difícil crear una subclase. A menos que vuelva a escribir UIButton, no hay mucho que pueda hacer en ese enfoque.

Cuando tuve que resolver un problema casi idéntico, se me ocurrió una vista que usa la composición para mostrar un proxy UIView, pero aún así permitir la intercepción táctil. Así es como funciona:

 @interface MyView : UIView { UIView * visibleView; } - (id) initWithFrame:(CGRect)frame controlClass:(Class)class; @end @implementation MyView - (id) initWithFrame:(CGRect)frame controlClass:(Class)class { if (self = [super initWithFrame:frame]) { CGRect visibleViewFrame = frame; visibleViewFrame.origin = CGPointZero; visibleView = [[class alloc] initWithFrame:visibleViewFrame]; [visibleView setUserInteractionEnabled:NO]; [self addSubview:visibleView]; [self setUserInteractionEnabled:YES]; [self setBackgroundColor:[UIColor clearColor]]; } return self; } - (void) dealloc { [visibleView removeFromSuperview]; [visibleView release], visibleView = nil; [super dealloc]; } - (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { if (someCondition == YES) { [visibleView touchesBegan:touches withEvent:event]; } } //and similar methods for touchesMoved:, touchesEnded:, etc @end 

La idea básica de esto es que está insertando el botón (o lo que sea) en una clase contenedora (similar a lo que describió en su pregunta). Sin embargo, está deshabilitando la interacción en el botón, lo que significa que cuando un usuario toca el botón, el evento tocará “pasará” por el botón y en los botones que contienen la supervista (en este caso, su instancia de MyView). Desde allí, puede procesar el toque de forma adecuada. Si desea que el botón “responda” al toque, simplemente permita que el botón lo haga enviando el mensaje UIResponder apropiado. (Es posible que deba volver a habilitar userInteractionEnabled en el visibleView primero. No estoy seguro de eso).

Como mencioné anteriormente, utilicé este enfoque (no esta implementación exacta, pero este patrón) con bastante éxito, cuando necesitaba tener un botón en la interfaz, pero también capturaba el objeto UITouch en sí.

Puedo sugerir usar este enfoque en su lugar:

  UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapDetected:)]; tapRecognizer.numberOfTapsRequired = 1; tapRecognizer.numberOfTouchesRequired = 1; [self.view addGestureRecognizer:tapRecognizer]; 

Puede hacer esto desde la supervista, sin necesidad de crear elementos de interfaz de usuario personalizados.

Gracias por las sugerencias y respuestas informativas. Terminé simplemente usando la explicación que se muestra en esta página: (debajo de “Trick 1: Emulación de aplicaciones de fotos deslizando / acercando / desplazando con un solo UIScrollView”).

Creo que tendrías que subclase UIButton en este caso y anular las respuestas a los eventos de movimiento y tacto de UIResponder. Luego, puede reenviarlos a cualquier objeto que desee para asumir que la subclase UIButton podría llegar a él.

En este caso, los pasaría a la super visión del UIButton.

 @interface MyButton : UIButton @end @implementation MyButton - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { [[self superview] touchesMoved:touches withEvent:event]; } @end 

Ver la documentación de UIResponder