Come rendere un superview intercettare gli eventi di touch del pulsante?

Dì che ho questo codice:

#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 

C’è un modo per intercettare gli eventi touch che vengono inviati al pulsante? Quello che mi piacerebbe eventualmente fare è creare una sottoclass UIView che dica al suo controller di visualizzazione (o delegato, qualunque cosa) quando rileva uno swipe in modo che possa “spingere” il prossimo controller di visualizzazione sullo stack (simile alla schermata iniziale di iPhone ). Ho pensato che questo fosse il primo passo, ma sono aperto a suggerimenti se mi sto avvicinando in modo errato.

Sarei interessato a vedere altre soluzioni, ma il modo più semplice che conosco è di ignorare uno di questi due metodi di UIView:

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

Questi metodi vengono chiamati per determinare se il touch rientra nei limiti della vista o in una qualsiasi delle sue sottoview , quindi questo è un buon punto per intercettare il touch e poi trasmetterlo. Fai quello che vuoi e poi

 return [super hitTest:point withEvent:event]; 

o

 return [super pointInside:point withEvent:event]; 

L’istinto direbbe “sottoclass UIButton”, ma UIButton è in realtà un cluster di class (ovvero, il tipo effettivo di object cambia in modo trasparente a seconda del tipo di pulsante che si desidera creare), il che rende estremamente difficile la sottoclass. A parte la riscrittura di UIButton, non c’è molto che tu possa fare su questo approccio.

Quando ho avuto la necessità di risolvere un problema quasi identico, mi è venuta in mente una vista che utilizza la composizione per visualizzare un UIView proxy, ma consente comunque l’intercettazione del touch. Ecco come funziona:

 @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 

L’idea di base è che stai incorporando il pulsante (o qualsiasi altra cosa) in una class contenitore (simile a ciò che hai descritto nella tua domanda). Tuttavia, stai disabilitando l’interazione sul pulsante, il che significa che quando un utente tocca il pulsante, l’evento di touch “sfonderà” il pulsante e nei pulsanti contenenti superview (in questo caso, l’istanza di MyView). Da lì, è ansible elaborare il touch in modo appropriato. Se si desidera che il pulsante “risponda” al touch, è sufficiente consentire al pulsante di farlo inviando il messaggio UIResponder appropriato. ( Potrebbe essere necessario ritriggersre prima userInteractionEnabled su visibleView.Per questo non sono sicuro.)

Come ho detto sopra, ho usato questo approccio (non questa esatta implementazione, ma questo pattern) con abbastanza successo, quando avevo bisogno di avere un pulsante sull’interfaccia, ma anche di catturare l’object UITouch stesso.

Posso suggerire di utilizzare questo approccio, invece:

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

Puoi farlo dal superview, non è necessario creare elementi dell’interfaccia utente personalizzati.

Grazie per i suggerimenti e le risposte informative. Ho finito usando solo la spiegazione mostrata in questa pagina: (sotto “Trucco 1: Emulazione foto app scorrendo / zoomando / scorrendo con un singolo UIScrollView”).

Credo che in questo caso avresti dovuto sottostare UIButton e ignorare le risposte agli eventi di touch e di movimento di UIResponder. Potresti quindi inoltrarli a qualsiasi object tu volessi assumere supponendo che la sottoclass UIButton possa raggiungerla.

In questo caso le passeresti nella superview di UIButton.

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

Vedi la documentazione di UIResponder