2009-06-23 15 views
34

Come posso rilevare un doppio tocco su una determinata cella in UITableView?Come posso rilevare un doppio tocco su una determinata cella in UITableView?

Voglio eseguire un'azione se l'utente ha effettuato un singolo tocco e un altro se un utente ha effettuato un doppio tocco? Devo anche conoscere un percorso indice in cui è stato effettuato il tocco.

Come posso raggiungere questo obiettivo?

Grazie.

+1

vuoi dire un doppio tocco o un multi-touch? – lostInTransit

+0

Intendo doppio tocco. –

+1

solo da un punto di vista HIG, potresti prendere in considerazione l'utilizzo di un pulsante accessorio invece di richiedere il doppio tocco? Non conosco lo scenario di utilizzo, ma potresti doverlo spiegare ai tuoi utenti. –

risposta

30

Se non si desidera creare una sottoclasse di UITableView, utilizzare un timer con della vista tabella didSelectRowAtIndex:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 
    //checking for double taps here 
    if(tapCount == 1 && tapTimer != nil && tappedRow == indexPath.row){ 
     //double tap - Put your double tap code here 
     [tapTimer invalidate]; 
     [self setTapTimer:nil]; 

     UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Double Tap" message:@"You double-tapped the row" delegate:self cancelButtonTitle:nil otherButtonTitles:@"OK", nil]; 
     [alert show]; 
     [alert release]; 
    } 
    else if(tapCount == 0){ 
     //This is the first tap. If there is no tap till tapTimer is fired, it is a single tap 
     tapCount = tapCount + 1; 
     tappedRow = indexPath.row; 
     [self setTapTimer:[NSTimer scheduledTimerWithTimeInterval:0.2 target:self selector:@selector(tapTimerFired:) userInfo:nil repeats:NO]]; 
    } 
    else if(tappedRow != indexPath.row){ 
     //tap on new row 
     tapCount = 0; 
     if(tapTimer != nil){ 
      [tapTimer invalidate]; 
      [self setTapTimer:nil]; 
     } 
    } 
} 

- (void)tapTimerFired:(NSTimer *)aTimer{ 
    //timer fired, there was a single tap on indexPath.row = tappedRow 
    if(tapTimer != nil){ 
     tapCount = 0; 
     tappedRow = -1; 
    } 
} 

HTH

+0

Mille grazie, è quello che ho trovato. –

+0

Ti dispiacerebbe postare altro codice? Dove stai impostando tapTimer, tappedRow e tapCount? Grazie! – Jonah

+0

È sicuro impostare il timer sul numero magico 0,2? È il solito limite di tempo tra i tocchi di un doppio tocco e può cambiare quel numero? – jlstrecker

1

Avrete probabilmente bisogno di sottoclasse UITableView e ignorare tutto ciò che tocco gli eventi siano appropriati (touchesBegan:withEvent;, touchesEnded:withEvent, etc.) Controllare gli eventi per vedere quanti tocchi ci sono stati, e fare il vostro comportamento personalizzato. Non dimenticare di chiamare attraverso i metodi di tocco UITableView's, altrimenti non otterrai il comportamento predefinito.

27

Override nella classe UITableView questo metodo

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

    if(((UITouch *)[touches anyObject]).tapCount == 2) 
    { 
    NSLog(@"DOUBLE TOUCH"); 
    } 
    [super touchesEnded:touches withEvent:event]; 
} 
+0

Potresti spiegarmi come faccio a ignorare esattamente il metodo? Ho creato un oggetto che eredita da UITableView e aggiunto un metodo consigliato. Ma questo metodo non viene chiamato. Grazie. –

+2

Non per necropost di per sé, ma questo è esattamente il tipo di cosa che stavo cercando - grazie! Solo una cosa per me. Cosa succede se l'UITableView in questione è quello all'interno di moreNavigationController di UITabBar? :( –

+3

Puoi anche aggiungere questo alla tua classe 'UITableViewCell' –

9

Nella sottoclasse UITableView, fai qualcosa di simile a questo:

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event 
{ 
    for (UITouch* touch in touches) { 
     if (touch.tapCount == 2) 
     { 
      CGPoint where = [touch locationInView:self]; 
      NSIndexPath* ip = [self indexPathForRowAtPoint:where]; 
      NSLog(@"double clicked index path: %@", ip); 

      // do something useful with index path 'ip' 
     } 
    } 

    [super touchesEnded:touches withEvent:event]; 
} 
6
if([touch tapCount] == 1) 
{ 
    [self performSelector:@selector(singleTapRecevied) withObject:self afterDelay:0.3]; 

} else if ([touch tapCount] == 2) 
    {   
    [TapEnableImageView cancelPreviousPerformRequestsWithTarget:self selector:@selector(singleTapRecevied) object:self]; 
} 

Utilizzare performSelector per chiamare un selettore anziché utilizzare un timer. Questo risolve il problema menzionato da @ V1ru8.

8

prima definire:

int tapCount; 
NSIndexPath *tableSelection; 

come variabili livello di classe nel file h e fare tutte le impostazioni necessarie in su. Poi ...

- (void)tableView(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 
    tableSelection = indexPath; 
    tapCount++; 

    switch (tapCount) { 
     case 1: //single tap 
      [self performSelector:@selector(singleTap) withObject: nil afterDelay: .4]; 
      break; 
     case 2: //double tap 
      [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(singleTap) object:nil]; 
      [self performSelector:@selector(doubleTap) withObject: nil]; 
      break; 
     default: 
      break; 
    } 
} 

#pragma mark - 
#pragma mark Table Tap/multiTap 

- (void)singleTap { 
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Alert" message:@"Single tap detected" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; 
    [alert show]; 
    tapCount = 0; 
} 

- (void)doubleTap { 
    NSUInteger row = [tableSelection row]; 
    companyName = [self.suppliers objectAtIndex:row]; 
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Alert" message:@"DoubleTap" delegate:nil cancelButtonTitle:@"Yes" otherButtonTitles: nil]; 
    [alert show]; 
    tapCount = 0; 
} 
+1

puoi semplicemente '[auto doubleTap];' invece di '[self performSelector: @selector (doubleTap) withObject: nil];';) – zxcat

+1

Questo codice ha alcuni errori di sintassi, provato a modificare ma per qualche strana ragione è stato rifiutato : http://stackoverflow.com/review/suggested-edits/2016633 – newenglander

6

ho scelto per la sua attuazione sovrascrivendo il UITableViewCell.

MyTableViewCell.h

@interface MyTableViewCell : UITableViewCell 

@property (nonatomic, assign) int numberOfClicks; 

@end 

MyTableViewCell.m

@implementation MyTableViewCell 

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { 
    UITouch *aTouch = [touches anyObject]; 
    self.numberOfClicks = [aTouch tapCount]; 
    [super touchesEnded:touches withEvent:event]; 
} 

TableViewController.m

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 

    MyTableViewCell *myCell = (MyTableViewCell*) [self.tableView cellForRowAtIndexPath:indexPath]; 

    NSLog(@"clicks:%d", myCell.numberOfClicks); 

    if (myCell.numberOfClicks == 2) { 
     NSLog(@"Double clicked"); 
    } 
} 
0

Nota: si prega di vedere i commenti qui sotto per vedere se mentre questa soluzione ha funzionato per me, potrebbe ancora non essere una buona idea.

Un'alternativa alla creazione di una sottoclasse di UITableView o UITableViewCell (e all'utilizzo di un timer) sarebbe solo per estendere la classe UITableViewCell con una categoria, ad esempio, (usando la risposta @ di ossigeno, in questo caso per la cella al posto del tabella):

@implementation UITableViewCell (DoubleTap) 
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event 
{ 
    if(((UITouch *)[touches anyObject]).tapCount == 2) 
    { 
     NSLog(@"DOUBLE TOUCH"); 
    } 
    [super touchesEnded:touches withEvent:event]; 
} 
@end 

In questo modo non c'è bisogno di andare in giro a rinominare le istanze esistenti di UITableViewCell con il nuovo nome della classe (si estenderà tutte le istanze della classe).

Nota che ora super in questo caso (trattandosi di una categoria) non si riferisce a UITableView ma al suo super, UITView. Ma la chiamata al metodo effettivo a touchesEnded:withEvent: è in UIResponder (di cui sia UITView e UITableViewCell sono sottoclassi), quindi non c'è alcuna differenza lì.

+0

La sovrascrittura di un metodo esistente in una categoria può o non può funzionare. – vikingosegundo

+0

http://stackoverflow.com/questions/5272451/overriding-methods-using-categories-in-objective-c – vikingosegundo

+0

L'originale 'UITableViewCell' non è una categoria, quindi non vedo come questo avrebbe un effetto Qui. Ma ho aggiornato l'ultima parte della mia risposta per renderla più chiara. – newenglander

0

Ecco la mia soluzione completa:

CustomTableView.h

// 
// CustomTableView.h 
// 

#import <UIKit/UIKit.h> 

@interface CustomTableView : UITableView 

    // Nothing needed here 

@end 

CustomTableView.m

// 
// CustomTableView.m 
// 

#import "CustomTableView.h" 

@implementation CustomTableView 


// 
// Touch event ended 
// 
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event 
{ 

    // For each event received 
    for (UITouch * touch in touches) { 

     NSIndexPath * indexPath = [self indexPathForRowAtPoint: [touch locationInView:self] ]; 

     // One tap happened 
     if([touch tapCount] == 1) 
     { 
      // Call the single tap method after a delay 
      [self performSelector: @selector(singleTapReceived:) 
         withObject: indexPath 
         afterDelay: 0.3]; 
     } 


     // Two taps happened 
     else if ([touch tapCount] == 2) 
     { 
      // Cancel the delayed call to the single tap method 
      [NSObject cancelPreviousPerformRequestsWithTarget: self 
                selector: @selector(singleTapReceived:) 
                 object: indexPath ]; 

      // Call the double tap method instead 
      [self performSelector: @selector(doubleTapReceived:) 
         withObject: indexPath ]; 
     } 


    } 

    // Pass the event to super 
    [super touchesEnded: touches 
       withEvent: event]; 

} 


// 
// Single Tap 
// 
-(void) singleTapReceived:(NSIndexPath *) indexPath 
{ 
    NSLog(@"singleTapReceived - row: %ld",(long)indexPath.row); 
} 


// 
// Double Tap 
// 
-(void) doubleTapReceived:(NSIndexPath *) indexPath 
{ 
    NSLog(@"doubleTapReceived - row: %ld",(long)indexPath.row); 
} 



@end 
0

Miglioramento per oxigen risposta.

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { 
    UITouch *touch = [touches anyObject]; 
    if(touch.tapCount == 2) { 
     CGPoint touchPoint = [touch locationInView:self]; 
     NSIndexPath *touchIndex = [self indexPathForRowAtPoint:touchPoint]; 
     if (touchIndex) { 
      // Call some callback function and pass 'touchIndex'. 
     } 
    } 
    [super touchesEnded:touches withEvent:event]; 
} 
2

Un'altra risposta

int touches; 

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    touches++; 

    if(touches==2){ 
     //your action 
    } 
} 

- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    touches=0; 
} 
+1

risposta migliore e più breve di sempre! –

1

Secondo @lostInTransit ho preparato codice Swift

var tapCount:Int = 0 
var tapTimer:NSTimer? 
var tappedRow:Int? 

override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { 
    //checking for double taps here 
    if(tapCount == 1 && tapTimer != nil && tappedRow == indexPath.row){ 
     //double tap - Put your double tap code here 
     tapTimer?.invalidate() 
     tapTimer = nil 
    } 
    else if(tapCount == 0){ 
     //This is the first tap. If there is no tap till tapTimer is fired, it is a single tap 
     tapCount = tapCount + 1; 
     tappedRow = indexPath.row; 
     tapTimer = NSTimer.scheduledTimerWithTimeInterval(0.2, target: self, selector: "tapTimerFired:", userInfo: nil, repeats: false) 
    } 
    else if(tappedRow != indexPath.row){ 
     //tap on new row 
     tapCount = 0; 
     if(tapTimer != nil){ 
      tapTimer?.invalidate() 
      tapTimer = nil 
     } 
    } 
} 

func tapTimerFired(aTimer:NSTimer){ 
//timer fired, there was a single tap on indexPath.row = tappedRow 
    if(tapTimer != nil){ 
     tapCount = 0; 
     tappedRow = -1; 
    } 
} 
0

Swift 3 soluzione da confrontare le risposte. Non sono necessarie estensioni, basta aggiungere questo codice.

override func viewDidLoad() { 
    viewDidLoad() 

    let doubleTapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleDoubleTap(sender:))) 
    doubleTapGestureRecognizer.numberOfTapsRequired = 2 
    tableView.addGestureRecognizer(doubleTapGestureRecognizer) 

    let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTapGesture(sender:))) 
    tapGestureRecognizer.numberOfTapsRequired = 1 
    tapGestureRecognizer.require(toFail: doubleTapGestureRecognizer) 
    tableView.addGestureRecognizer(tapGestureRecognizer) 
} 

func handleTapGesture(sender: UITapGestureRecognizer) { 
    let touchPoint = sender.location(in: tableView) 
    if let indexPath = tableView.indexPathForRow(at: touchPoint) { 
     print(indexPath) 
    } 
} 

func handleDoubleTap(sender: UITapGestureRecognizer) { 
    let touchPoint = sender.location(in: tableView) 
    if let indexPath = tableView.indexPathForRow(at: touchPoint) { 
     print(indexPath) 
    } 
} 
0

Questa soluzione funziona solo per la cella UICollectionView o UITableView.

prima dichiarare queste variabili

int NUMBER_OF_CLICKS;

BOOL thread_started;

Poi mettere questo codice nel tuo didSelectItemAtIndexPath

++number_of_clicks; 
if (!thread_started) { 

    thread_started = YES; 

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 
           0.25 * NSEC_PER_SEC), 
        dispatch_get_main_queue(),^{ 

         if (number_of_clicks == 1) { 
          ATLog(@"ONE"); 
         }else if(number_of_clicks == 2){ 
          ATLog(@"DOUBLE"); 
         } 

         number_of_clicks = 0; 
         thread_started = NO; 

        }); 

     } 

0,25 è il ritardo tra 2 clic. Penso che 0.25 sia perfetto per rilevare questo tipo di clic. Ora è possibile rilevare solo un clic e due clic separatamente. Good Luck

Problemi correlati