Long press on UITableView

IosIphoneUitableviewGesture RecognitionLong Press

Ios Problem Overview


I would like to handle a long press on a UITableViewCell to print a "quick access menu". Did someone already do this?

Particularly the gesture recognize on UITableView?

Ios Solutions


Solution 1 - Ios

First add the long press gesture recognizer to the table view:

UILongPressGestureRecognizer *lpgr = [[UILongPressGestureRecognizer alloc] 
  initWithTarget:self action:@selector(handleLongPress:)];
lpgr.minimumPressDuration = 2.0; //seconds
lpgr.delegate = self;
[self.myTableView addGestureRecognizer:lpgr];
[lpgr release];

Then in the gesture handler:

-(void)handleLongPress:(UILongPressGestureRecognizer *)gestureRecognizer
{
    CGPoint p = [gestureRecognizer locationInView:self.myTableView];

    NSIndexPath *indexPath = [self.myTableView indexPathForRowAtPoint:p];
    if (indexPath == nil) {
        NSLog(@"long press on table view but not on a row");
    } else if (gestureRecognizer.state == UIGestureRecognizerStateBegan) {
        NSLog(@"long press on table view at row %ld", indexPath.row);
    } else {
        NSLog(@"gestureRecognizer.state = %ld", gestureRecognizer.state);
    }
}

You have to be careful with this so that it doesn't interfere with the user's normal tapping of the cell and also note that handleLongPress may fire multiple times (this will be due to the gesture recognizer state changes).

Solution 2 - Ios

I've used Anna-Karenina's answer, and it works almost great with a very serious bug.

If you're using sections, long-pressing the section title will give you a wrong result of pressing the first row on that section, I've added a fixed version below (including the filtering of dummy calls based on the gesture state, per Anna-Karenina suggestion).

- (IBAction)handleLongPress:(UILongPressGestureRecognizer *)gestureRecognizer
{
	if (gestureRecognizer.state == UIGestureRecognizerStateBegan) {

		CGPoint p = [gestureRecognizer locationInView:self.tableView];
		
		NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:p];
		if (indexPath == nil) {
			NSLog(@"long press on table view but not on a row");
		} else {
			UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
			if (cell.isHighlighted) {
				NSLog(@"long press on table view at section %d row %d", indexPath.section, indexPath.row);
			}
		}
	}
}

Solution 3 - Ios

Answer in Swift 5 (Continuation of Ricky's answer in Swift)

> Add the UIGestureRecognizerDelegate to your ViewController

 override func viewDidLoad() {
    super.viewDidLoad()
    
    //Long Press
    let longPressGesture = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPress))
    longPressGesture.minimumPressDuration = 0.5
    self.tableView.addGestureRecognizer(longPressGesture)
 }

 

> And the function:

@objc func handleLongPress(longPressGesture: UILongPressGestureRecognizer) {
    let p = longPressGesture.location(in: self.tableView)
    let indexPath = self.tableView.indexPathForRow(at: p)
    if indexPath == nil {
        print("Long press on table view, not row.")
    } else if longPressGesture.state == UIGestureRecognizer.State.began {
        print("Long press on row, at \(indexPath!.row)")
    }
}

Solution 4 - Ios

Here are clarified instruction combining Dawn Song's answer and Marmor's answer.

Drag a long Press Gesture Recognizer and drop it into your Table Cell. It will jump to the bottom of the list on the left.

enter image description here

Then connect the gesture recognizer the same way you would connect a button. enter image description here

Add the code from Marmor in the the action handler

- (IBAction)handleLongPress:(UILongPressGestureRecognizer *)sender {
if (sender.state == UIGestureRecognizerStateBegan) {
	
	CGPoint p = [sender locationInView:self.tableView];
	
	NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:p];
	if (indexPath == nil) {
		NSLog(@"long press on table view but not on a row");
	} else {
		UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
		if (cell.isHighlighted) {
			NSLog(@"long press on table view at section %d row %d", indexPath.section, indexPath.row);
		}
	}
}

}

Solution 5 - Ios

Looks to be more efficient to add the recognizer directly to the cell as shown here:

Tap&Hold for TableView Cells, Then and Now

(scroll to the example at the bottom)

Solution 6 - Ios

Answer in Swift:

Add delegate UIGestureRecognizerDelegate to your UITableViewController.

Within UITableViewController:

override func viewDidLoad() {
    super.viewDidLoad()
    
    let longPressGesture:UILongPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: "handleLongPress:")
    longPressGesture.minimumPressDuration = 1.0 // 1 second press
    longPressGesture.delegate = self
    self.tableView.addGestureRecognizer(longPressGesture)

}

And the function:

func handleLongPress(longPressGesture:UILongPressGestureRecognizer) {

    let p = longPressGesture.locationInView(self.tableView)
    let indexPath = self.tableView.indexPathForRowAtPoint(p)
    
    if indexPath == nil {
        print("Long press on table view, not row.")
    }
    else if (longPressGesture.state == UIGestureRecognizerState.Began) {
        print("Long press on row, at \(indexPath!.row)")
    }
    
}

Solution 7 - Ios

I put together a little category on UITableView based on Anna Karenina's excellent answer.

Like this you'll have a convenient delegate method like you're used to when dealing with regular table views. Check it out:

//  UITableView+LongPress.h

#import <UIKit/UIKit.h>

@protocol UITableViewDelegateLongPress;

@interface UITableView (LongPress) <UIGestureRecognizerDelegate>
@property(nonatomic,assign)   id <UITableViewDelegateLongPress>   delegate;
- (void)addLongPressRecognizer;
@end


@protocol UITableViewDelegateLongPress <UITableViewDelegate>
- (void)tableView:(UITableView *)tableView didRecognizeLongPressOnRowAtIndexPath:(NSIndexPath *)indexPath;
@end



//  UITableView+LongPress.m

#import "UITableView+LongPress.h"

@implementation UITableView (LongPress)
@dynamic delegate;

- (void)addLongPressRecognizer {
    UILongPressGestureRecognizer *lpgr = [[UILongPressGestureRecognizer alloc]
                                          initWithTarget:self action:@selector(handleLongPress:)];
    lpgr.minimumPressDuration = 1.2; //seconds
    lpgr.delegate = self;
    [self addGestureRecognizer:lpgr];
}


- (void)handleLongPress:(UILongPressGestureRecognizer *)gestureRecognizer
{
    CGPoint p = [gestureRecognizer locationInView:self];

    NSIndexPath *indexPath = [self indexPathForRowAtPoint:p];
    if (indexPath == nil) {
        NSLog(@"long press on table view but not on a row");
    }
    else {
        if (gestureRecognizer.state == UIGestureRecognizerStateBegan) {
            // I am not sure why I need to cast here. But it seems to be alright.
            [(id<UITableViewDelegateLongPress>)self.delegate tableView:self didRecognizeLongPressOnRowAtIndexPath:indexPath];
        }
    }
}

If you want to use this in a UITableViewController, you probably need to subclass and conform to the new protocol.

It works great for me, hope it helps others!

Solution 8 - Ios

Swift 3 answer, using modern syntax, incorporating other answers, and eliminating unneeded code.

override func viewDidLoad() {
    super.viewDidLoad()
    let recognizer = UILongPressGestureRecognizer(target: self, action: #selector(tablePressed))
    tableView.addGestureRecognizer(recognizer)
 }

@IBAction func tablePressed(_ recognizer: UILongPressGestureRecognizer) {
    let point = recognizer.location(in: tableView)
    
    guard recognizer.state == .began,
          let indexPath = tableView.indexPathForRow(at: point),
          let cell = tableView.cellForRow(at: indexPath),
          cell.isHighlighted
    else {
        return
    }
    
    // TODO
}

Solution 9 - Ios

Just add UILongPressGestureRecognizer to the given prototype cell in storyboard, then pull the gesture to the viewController's .m file to create an action method. I made it as I said.

Solution 10 - Ios

Use the UITouch timestamp property in touchesBegan to launch a timer or stop it when touchesEnded got fired

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionfoOgView Question on Stackoverflow
Solution 1 - Iosuser467105View Answer on Stackoverflow
Solution 2 - IosmarmorView Answer on Stackoverflow
Solution 3 - IosBenView Answer on Stackoverflow
Solution 4 - IosRyan HeitnerView Answer on Stackoverflow
Solution 5 - IosJ.R.View Answer on Stackoverflow
Solution 6 - IosRickyView Answer on Stackoverflow
Solution 7 - Iosde.View Answer on Stackoverflow
Solution 8 - IosphatmannView Answer on Stackoverflow
Solution 9 - IosDawnSongView Answer on Stackoverflow
Solution 10 - IosThomas JoulinView Answer on Stackoverflow