2009-08-15 10 views
6

Dire che ho questo codice:Come rendere un superview intercettare gli eventi di tocco del pulsante?

#import <UIKit/UIKit.h> 

@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 <UIApplicationDelegate> 
{ 
    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 

Esiste un modo per intercettare gli eventi di tocco sempre inviati al pulsante? Quello che mi piacerebbe eventualmente fare è creare una sottoclasse 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.

risposta

1

Grazie per i suggerimenti e le risposte informative. Ho finito per usare la spiegazione mostrata nella pagina this: (in "Trucco 1: Emulazione foto app scorrendo/zoomando/scorrendo con un singolo UIScrollView").

16

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

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

Questi metodi vengono chiamati per determinare se il tocco è all'interno del limiti della vista o una delle sue sottoview, quindi questo è un buon punto per intercettare il tocco e poi passarlo. Basta fare quello che vuoi, e poi

return [super hitTest:point withEvent:event]; 

o

return [super pointInside:point withEvent:event]; 
+0

non intendi return [superview hitTest: point withEvent: event]; –

+0

'super' ha ragione perché vorrete chiamare il metodo di classe genitore delle classi,' superview' salterà su questo. – keegan3d

0

credo che avrebbe dovuto creare una sottoclasse UIButton in questo caso e ignorare le risposte al tatto e di movimento eventi di UIResponder. Potresti quindi inoltrarli a qualsiasi oggetto tu volessi assumere supponendo che la sottoclasse UIButton possa raggiungerla.

In questo caso li si passa alla superview di UIButton.

@interface MyButton : UIButton 
@end 

@implementation MyButton 

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event 
{ 
    [[self superview] touchesMoved:touches withEvent:event]; 
} 

@end 

Vedi UIResponder documentation

+1

UIButton è un cluster di classe, sfortunatamente. Ciò significa che è quasi impossibile creare una sottoclasse. –

+0

Non sapevo che fosse così male. Ha menzionato solo alcune sottoclassi di UIView, quindi stavo andando con UIButton da quando lo ha usato nel suo codice di esempio. – criscokid

4

Instinct direbbe "sottoclasse UIButton", ma UIButton è in realtà un cluster di classe (ad esempio, il tipo effettivo di oggetto trasparente cambia a seconda del tipo di pulsante che si vuole fare), il che rende estremamente difficile la sottoclasse. A parte la riscrittura di UIButton, non c'è molto che tu possa fare su questo approccio.

Quando ho avuto bisogno 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 tocco. 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 di questo è che si sta incorporando il tasto (o altro) in una classe contenitore (simile a quello che hai descritto nella sua domanda). Tuttavia, stai disabilitando l'interazione sul pulsante, il che significa che quando un utente tocca il pulsante, l'evento di tocco "sfonderà" il pulsante e nei pulsanti contenenti superview (in questo caso, l'istanza di MyView). Da lì, è possibile elaborare il tocco in modo appropriato. Se si desidera che il pulsante "risponda" al tocco, è sufficiente consentire al pulsante di farlo inviando il messaggio UIResponder appropriato. (Si potrebbe necessità di riattivare userInteractionEnabled sul visibleView prima, però. Io sono sicuro di questo.)

Come ho già detto, ho usato questo metodo (non questo esatto implementazione, ma questo modello) abbastanza bene, quando avevo bisogno di avere un pulsante sull'interfaccia, ma anche di catturare l'oggetto UITouch stesso.

3

mi permetto di suggerire utilizzando questo approccio, invece:

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

    [self.view addGestureRecognizer:tapRecognizer]; 

Si può fare questo dal superview, non c'è bisogno di fare gli elementi di interfaccia utente personalizzata.

+0

Nice, UITapGestureRecognizer ha funzionato alla grande con un UIView che contiene diversi UILabel. – joelsand

Problemi correlati