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.
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
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];
}
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];
}
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;
}
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.
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");
}
}
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;
}
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)
}
}
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.
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;
}
}
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];
}
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.
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