Como fazer um superview interceptar events de toque de botão?

Digamos que eu tenha 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 

Existe alguma maneira de interceptar os events de toque enviados para o botão? O que eu gostaria de fazer é criar uma subclass UIView que diga ao seu controlador de visualização (ou delegado, o que for) quando detectar um golpe para poder “empurrar” o próximo controlador de visualização para a pilha (semelhante à canvas inicial do iPhone) ). Eu percebi que este foi o primeiro passo, mas estou aberto a sugestões se estou abordando isso incorretamente.

Eu estaria interessado em ver outras soluções, mas a maneira mais fácil que eu conheço é sobrepor qualquer um desses dois methods do UIView:

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

Esses methods são chamados para determinar se o toque está dentro dos limites da visualização ou de qualquer uma de suas subvisualizações , portanto, esse é um bom ponto para interceptar o toque e, em seguida, transmiti-lo. Apenas faça o que você quiser e depois

 return [super hitTest:point withEvent:event]; 

ou

 return [super pointInside:point withEvent:event]; 

O instinto diria “subclass UIButton”, mas o UIButton é na verdade um cluster de class (ou seja, o tipo real de object transparentemente muda dependendo do tipo de botão que você deseja criar), o que torna extremamente difícil subclass. Curta de rewrite o UIButton, não há muito o que fazer nessa abordagem.

Quando precisei resolver um problema quase idêntico, desenvolvi uma visão que usa composição para exibir um proxy UIView, mas ainda permite interceptação por toque. Veja como isso 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 

A idéia básica para isso é que você está incorporando o botão (ou qualquer outro) em uma class de contêiner (semelhante ao que você descreveu na sua pergunta). No entanto, você está desabilitando a interação no botão, o que significa que quando um usuário toca no botão, o evento de tap “percorrerá” o botão e os botões que contêm superview (nesta, sua instância do MyView). A partir daí, você pode processar o toque de maneira apropriada. Se você quiser que o botão “responda” ao toque, apenas permita que o botão faça isso enviando a mensagem UIResponder apropriada. (Você pode precisar reativar o userInteractionEnabled no visibleView primeiro, no entanto. Não tenho certeza disso.)

Como afirmei acima, usei essa abordagem (não exatamente essa implementação, mas esse padrão) com bastante êxito, quando precisei ter um botão na interface, mas também capturar o próprio object UITouch.

Posso sugerir usar esta abordagem em vez disso:

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

Você pode fazer isso a partir do superview, sem necessidade de criar elementos de interface personalizados.

Obrigado pelas sugestões e respostas informativas. Acabei de usar apenas a explicação mostrada nesta página: (em “Truque 1: Emulando fotos, deslize / deslize / deslize com um único UIScrollView”).

Eu acredito que você teria que subclassificar o UIButton neste caso e replace as respostas aos events de toque e movimento do UIResponder. Você poderia, então, encaminhá-los para qualquer object que você quisesse presumir que a subclass UIButton pudesse chegar a ele.

Neste caso, você os passaria para a super visão do UIButton.

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

Veja a documentação do UIResponder