user14764 user14764 - 4 months ago 9x
iOS Question

How to differenciate indexPath of first cell with indexPath of empty space at the end of table?

I use the following code to detect a click on a UITableView and take action depending on which cell is clicked, and which element in the cell was clicked, with a default action for any element that doesn't match.

-(void)addTapRecognizer {
// this is called when the view is created
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)];
singleTap.delegate = self;
singleTap.numberOfTapsRequired = 1;
singleTap.numberOfTouchesRequired = 1;
[self.tableView addGestureRecognizer:singleTap];

- (void)handleTap:(UITapGestureRecognizer *)tap {
NSLog(@"tap detected!");
if (UIGestureRecognizerStateEnded != tap.state) {
UITableView *tableView = (UITableView *)tap.view;
CGPoint p = [tap locationInView:tap.view];
NSIndexPath* indexPath = [tableView indexPathForRowAtPoint:p];
[tableView deselectRowAtIndexPath:indexPath animated:NO];

NSLog(@"selectedIndex = %ld", (long)indexPath.row);
// take action depending where the cell was clicked
// with a default action if no element matches
MyTableViewCell *cell = (MyTableViewCell *) [self.tableView cellForRowAtIndexPath:indexPath];
CGPoint pointInCell = [tap locationInView:cell];
if(CGRectContainsPoint(cell.someImage.frame,pointInCell)) {
[self openItemID:[ItemList[indexPath.row] valueForKey:ID_ITEM]];
if (...) {
[self openItemID:[ItemList[indexPath.row] valueForKey:ID_ITEM]];

My problem is that when there aren't enough cells to fill the screen (so for instance the table contains 2 cells and then blank space below), when the user clicks below the last cell, this is treated as a click on the first cell (the console logs "selectedIndex = 0" in both cases).

Is there a way to tell the difference between such a click, in the empty space at the end of the table, and a click on a "proper" cell of the table?


Is there a way to tell the difference between such a click, in the empty space at the end of the table, and a click on a "proper" cell of the table?

Yes. For the cheap and easy solution only do what you are trying to do if you actually get an indexPath:

 NSIndexPath *indexPath = [tableView indexPathForRowAtPoint:p];
 if(indexPath != nil){
     // do everything in here

Basically, your indexPath is returning nil because it can't find a row. From the docs:

An index path representing the row and section associated with point, or nil if the point is out of the bounds of any row.

You could do it the way that you're currently doing it but is there any reason why you aren't using:

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

This is a much more standard way of detecting the cell that the user tapped on.

This method won't be called if you tap on something that isn't a cell and has a number of other benefits. Firstly you get a reference to the tableView and the indexPath for free. But you also won't need any gesture recognisers this way either.

Try something like this:

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
    // Do stuff with cell here...

Obviously this all assumes that you have correctly set a class as your table view's delegate.

NB: It's very easy to mistakenly write didDeselectRowAtIndexPath instead of didSelectRowAtIndexPath when using Xcode's autocompletion to do this. I always do this and then inevitably realise my mistake 20 minutes later.