web-dev-qa-db-fra.com

Comment détecter un double tap sur une certaine cellule dans UITableView?

Comment détecter un double tap sur une certaine cellule dans UITableView?

i.e. Je souhaite effectuer une action si l'utilisateur a touché une seule touche et une autre si un utilisateur a effectué une double pression? J'ai également besoin de connaître un chemin d'index où le contact a été effectué.

Comment puis-je atteindre cet objectif?

Merci.

34
Ilya Suzdalnitski

Si vous ne souhaitez pas créer une sous-classe de UITableView, utilisez une minuterie avec le didSelectRowAtIndex: de la vue de table.

- (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

31
lostInTransit

Remplacez cette méthode par votre classe UITableView

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

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

Dans votre sous-classe UITableView, procédez comme suit:

- (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];
}
9
Matt Connolly

D'abord définir:

int tapCount;
NSIndexPath *tableSelection;

en tant que variables de niveau de classe dans le fichier .h et effectuez toute la configuration nécessaire. Ensuite...

- (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;
}
9
Vladimir Stazhilov
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]; 
}

Utilisez performSelector pour appeler un sélecteur au lieu d’utiliser une minuterie. Cela résout le problème mentionné par @ V1ru8.

6
Fourj

J'ai choisi de l'implémenter en remplaçant la 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");
   }
}
6
masam

Une autre réponse

int touches;

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

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

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

Swift 3 solution de comparer les réponses. Pas besoin d'extensions, il suffit d'ajouter ce code.

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)
    }
}
1
Kamil Harasimowicz

Vous aurez probablement besoin de sous-classer UITableView et de remplacer les événements tactiles appropriés (touchesBegan:withEvent ;, touchesEnded:withEvent, etc.). Inspectez les événements pour voir le nombre de contacts touchés et modifiez votre comportement personnalisé. N'oubliez pas d'appeler les méthodes tactiles UITableView's, sinon vous n'obtiendrez pas le comportement par défaut.

1
zpasternack

Selon @lostInTransit, j'ai préparé du code dans 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;
    }
}
1
Roman Barzyczak

Amélioration de oxigen answer.

- (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];
}
0
user1763487

Remarque: s'il vous plaît voir les commentaires ci-dessous pour voir si bien que cette solution a fonctionné pour moi, ce ne peut toujours pas être une bonne idée.

Une alternative à la création d'une sous-classe de UITableView ou UITableViewCell (et à l'utilisation d'un timer) serait simplement d'étendre la classe UITableViewCell avec une catégorie, par exemple (en utilisant la réponse de @ oxigen, dans ce cas pour la cellule au lieu du tableau):

@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

De cette façon, vous n'avez pas besoin de renommer les instances existantes de UITableViewCell avec le nouveau nom de classe (cela étendra toutes les instances de la classe).

Notez que maintenant super dans ce cas (il s’agit d’une catégorie) ne fait pas référence à UITableView mais à son super, UITView. Mais l'appel de méthode réel à touchesEnded:withEvent: est dans UIResponder (dont UITView et UITableViewCell sont des sous-classes), il n'y a donc aucune différence.

0
newenglander

Voici ma solution complète:

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
grigb