2012-08-12 16 views
5

Immagina la tua tastiera. Immagina di mettere un dito in basso di un tasto, poi (tenendo premuto) muovi il dito fino a un altro tasto sulla tastiera. Ora, immagina che ciascun tasto sulla tastiera sia un UIButton. quando si tiene premuto il dito sul tasto attuale, questo tasto (UIButton) è evidenziato. Quindi, quando l'utente passa a un'altra chiave, la prima chiave non viene più evidenziata e il tasto corrente premuto viene evidenziato.Come passare dall'uso di un UIButton a un altro mentre si tiene premuto il dito?

Ora, ho una griglia 6 x 8 di UIButton ogni 53 x 53 pixel. Quindi, ho 48 UIButtons. Voglio replicare questo tipo di idea. Il pulsante su cui è posizionato il dito dell'utente avrà un'immagine leggermente più chiara (per apparire selezionata come), e tutti gli altri non saranno leggermente più chiari.

Qui è la mia idea di come fare per questo,

1) Creare tutto 48 UIButtons in viewDidLoad. Aggiungi l'immagine più leggera a UIControlStateHighlighted per tutti UIButtons.

2) Aggiungi una sorta di target per touchUpOutside che in qualche modo rende il pulsante corrente non evidenziato e non utilizzabile. (forse impostare l'evidenziato e userInteractionEnabled su no). Ma come posso dire che il prossimo pulsante debba essere usato? E come posso dire che voglio lo specifico UIButton che le dita degli utenti sono sotto per essere evidenziato e in uso per rilevare gesti e cose.

Inoltre, questo metodo touchUpOutside potrebbe non funzionare perché tutti i pulsanti sono uno accanto all'altro e penso che si debba trascinare molto indietro per eseguire questo touchUpOutside.

È anche importante notare che i pulsanti non sono disposti in righe e colonne uguali. A volte un pulsante sarà in realtà un UIImage ma sembra un UIButton. Quindi, fare una sorta di enumerazione rapida confrontando i frame per qualche motivo non funzionerà.

+0

Descrivete alcuni comportamenti di basso livello (il fatto che se premete e trascinate il dito su un altro pulsante), ma non descrivete il problema più ampio che state cercando di risolvere, in modo che risponda un po 'alla vostra domanda difficile. Cosa stai cercando di fare? Basta rilevare il tocco in un pulsante e ritoccare in un altro? O un'intera serie in uno schema di digitazione simile a Swype? La risposta proposta potrebbe dipendere da ciò che stai cercando di fare. – Rob

+0

Inoltre, non capisco il tuo ultimo punto sull'enumerazione non veloce dei frame non funzionante (perché se sei preoccupato per diversi tipi di sottoview, verificherai "isKindOfClass" per sapere qual è il comportamento giusto). Vedi la mia risposta. – Rob

+0

Suppongo che intendessi UIImageView invece di UIImage – WolfLink

risposta

4

Due osservazioni:

  1. Io generalmente uso di riconoscimento dei gesti per cose come questa, non i vari touchUp... metodi.

  2. So che hai detto che non vuoi un'enumerazione veloce attraverso le sottoview, ma perché no? Solo perché alcune sono classi diverse rispetto ad altre non è un problema (perché si potrebbe semplicemente testare il metodo isKindOfClass). (E, a parte, non sono sicuro del motivo per cui alcuni potrebbero essere pulsanti e altri no.) Solo perché non si allineano davvero non fa alcuna differenza, neanche.

In ogni caso, per cose come questa, ho spesso metterò il sistema di riconoscimento gesto sulla vista padre condiviso (ad esempio, in genere vista del controller della vista), e quindi enumerare i subviews per vedere in cui la corrente CGPoint è contenuta ?

Non so se tutto questo ha senso per te, ma mi sembra più facile. Se chiarisci il tuo intento, forse potrei affinare ulteriormente la mia risposta.

Proprio come un esempio pratico, è possibile definire un riconoscimento di gesture continuo che tenesse traccia della prima e dell'ultima sottoview su cui è stato fatto clic (e se non si avvia il gesto su una sottoview, non viene generato alcun gesto, e se non la smetti il ​​vostro gesto su una visualizzazione secondaria, si annulla il tutto), ad esempio:

@interface SubviewGestureRecognizer : UIGestureRecognizer 

@property (nonatomic,strong) UIView *firstSubview; 
@property (nonatomic,strong) UIView *currentSubview; 

@end 

@implementation SubviewGestureRecognizer 

- (id) initWithTarget:(id)target action:(SEL)action 
{ 
    self = [super initWithTarget:target action:action]; 
    if (self) 
    { 
     self.firstSubview = nil; 
     self.currentSubview = nil; 
    } 

    return self; 
} 

// you might want to tweak this `identifySubview` to only look 
// for buttons and imageviews, or items with nonzero tag properties, 
// or recursively navigate if it encounters container UIViews 
// or whatever suits your app 

- (UIView *)identifySubview:(NSSet *)touches 
{ 
    CGPoint location = [[touches anyObject] locationInView:self.view]; 

    for (UIView *subview in self.view.subviews) 
    { 
     if (CGRectContainsPoint(subview.frame, location)) 
     { 
      return subview; 
     } 
    } 

    return nil; 
} 

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 
{ 
    [super touchesBegan:touches withEvent:event]; 

    if ([touches count] != 1) 
    { 
     self.state = UIGestureRecognizerStateFailed; 
     return; 
    } 

    self.firstSubview = [self identifySubview:touches]; 
    self.currentSubview = self.firstSubview; 

    if (self.firstSubview == nil) 
     self.state = UIGestureRecognizerStateFailed; 
    else 
     self.state = UIGestureRecognizerStateBegan; 
} 

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

    if (self.state == UIGestureRecognizerStateFailed) return; 

    self.currentSubview = [self identifySubview:touches]; 

    self.state = UIGestureRecognizerStateChanged; 
} 

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event 
{ 
    [super touchesEnded:touches withEvent:event]; 

    self.currentSubview = [self identifySubview:touches]; 

    if (self.currentSubview != nil) 
     self.state = UIGestureRecognizerStateEnded; 
    else 
     self.state = UIGestureRecognizerStateFailed; 
} 

- (void)reset 
{ 
    [super reset]; 

    self.firstSubview = nil; 
    self.currentSubview = nil; 
} 

è quindi possibile impostare questa funzione in viewDidLoad:

SubviewGestureRecognizer *recognizer = [[SubviewGestureRecognizer alloc] initWithTarget:self action:@selector(handleTouches:)]; 
[self.view addGestureRecognizer:recognizer]; 

E poi definire il gestore come tale (questo funziona con pulsanti e visualizzazioni di immagini, prendendo approfittando del fatto che entrambi supportano setHighlighted):

- (void)handleTouches:(SubviewGestureRecognizer *)sender 
{ 
    static UIControl *previousControl = nil; 

    if (sender.state == UIGestureRecognizerStateBegan || sender.state == UIGestureRecognizerStateChanged) 
    { 
     if (sender.state == UIGestureRecognizerStateBegan) 
      previousControl = nil; 

     UIView *subview = sender.currentSubview; 
     if (previousControl != subview) 
     { 
      // reset the old one (if any) 

      [previousControl setHighlighted:NO]; 

      // highlight the new one 

      previousControl = (UIControl *)subview; 
      [previousControl setHighlighted:YES]; 
     } 
    } 
    else if (sender.state == UIGestureRecognizerStateEnded) 
    { 
     if (previousControl) 
     { 
      [previousControl setHighlighted:NO]; 
      NSLog(@"successfully touchdown on %@ and touchup on %@", sender.firstSubview, sender.currentSubview); 
     } 
    } 
    else if (sender.state == UIGestureRecognizerStateCancelled || sender.state == UIGestureRecognizerStateFailed) 
    { 
     [previousControl setHighlighted:NO]; 
     NSLog(@"cancelled/failed gesture"); 
    } 
} 
+0

Beh, sono un po 'titubante a farlo in questo modo. Perché quando l'utente muove il dito, voglio che sia istantaneo. E, se eseguo questo metodo ogni volta che eseguo un piccolo trascinamento, penso che probabilmente si verificheranno dei problemi. – bmende

+0

Penso che troverai che è istantaneo, ma ovviamente persegui altri approcci, se vuoi. Ma al livello più elementare, penso che tutte queste tecniche, dietro le quinte, probabilmente usino gli stessi metodi 'UIResponder',' touchBegan', 'touchesMoved',' touchedEnded', ecc., Quindi non sono sicuro che sia fa molta differenza. Per inciso, ho aggiornato la mia risposta con un riconoscitore di gesti personalizzati che nasconde un sacco della logica "che sottovalvola ero sopra", quindi è un po 'più pulito nella routine del gestore del tocco. Ma certamente nessuna offesa sarà presa qui se trovi un'altra tecnica più di tuo gradimento. – Rob

+2

@Sasquatch A meno che tu non abbia migliaia di pulsanti, non mi preoccuperei. L'ottimizzazione prematura è la radice ... –

0

Ecco un modo abbastanza semplice per farlo. Supponendo che si stanno creando i pulsanti a livello di codice (sarebbe un enorme problema farlo con il generatore di interfacce). Questo metodo funziona in sottoclassi di UIViewController, UIView e UIGestureRecognizer.

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { 

    //get the touch object 
    UITouch *myTouch = [touches anyObject]; 

    //get the location of the touch 
    CGPoint *myPoint = [myTouch locationInView:self.view]; 

    //detect if the touch is in one of your buttons 
    if (CGRectContainsPoint(myButton.frame, myPoint){ 
     /*do whatever needs to be done when the button is pressed 
     repeat the CGRectContainsPoint for all buttons, 
     perhaps using a while or for loop to look through an array?*/ 
    } 

} 

Questo metodo solo rileva quando l'utente mette il dito verso il basso, è necessario utilizzare questi metodi per rilevare altri movimenti:

touchesMoved:withEvent rileva quando il dito si muove sullo schermo senza sollevare

touchesEnded:withEvent rileva quando il dito viene sollevato dallo schermo

touchesCancelled:withEvent: rileva quando qualcosa interrompe l'app, come ricevere una chiamata o bloccare lo schermo

Problemi correlati