Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I detect taps on a UITableView outside of UITableViewCells - i.e. in the section heading?

I want to handle a tap in a UITableView not including the cells. In other words, the headers and footers.

I can add a gesture recognizer to the headers (there are no footers), but the space at the bottom of the last section does not respond to a tap.

Instead of adding the gesture recognizers above, I tried adding a gesture recognizer to the table, but it prevents tableView:didSelectRowAtIndexPath: being called.

I tried all sorts of UIGestureRecognizerDelegate calls with not much luck.

I finally got that to work by setting cancelsTouchesInView = NO on the recognizer (I think this was the secret) and implementing gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer: in the delegate so that the tableView got touches and called tableView:didSelectRowAtIndexPath:

The handler has to filter out taps on the cells, but at least it works. (Also, found a bug in UITableView indexPathForRowAtPoint:point which returns invalid indexPaths sometimes.)

My question is: Is there a better way of getting the cell views to prevent touches/gestures getting through to the table? (I will post code if nobody gives me any better ideas.)

like image 634
Matt Avatar asked Jul 24 '14 06:07

Matt


2 Answers

If you don't need to understand - what was clicked (footer or header), you can try the following approach.

Add gesture recognizer in viewDidLoad method.

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

    NSIndexPath *indexPath = [self.messageTableView indexPathForRowAtPoint:p];
    if (indexPath == nil)
        NSLog(@"long press on table view but not on a row");
    else {
        NSLog(@"long press on table view on a row");
    }
}

Attention! I haven't tested this code:)

like image 114
Nikita Took Avatar answered Jan 21 '23 07:01

Nikita Took


Here is the code that I went with.

1 Add a tap gesture recognizer to the table. Set Cancels touches in view unchecked.

2 Allow the gestures through the table.

#pragma mark - UIGestureRecognizerDelegate

/**
 * Prevents the tap gesture recognizer in the table from gobbling taps
 * and allows the table to perform tableView:didSelectRowAtIndexPath:
 * <p>
 * The gesture recognizer must have cancelsTouchesInView == NO to allow
 * the touches through to the table.
 * <p>
 * The action must check that the tap occurred outside of cells.
 *
 * @see handleTap:
 */
- (BOOL) gestureRecognizer:(UIGestureRecognizer*)recognizer
  shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer*)o {
    return recognizer.view == self.table;
}

3 Check the tap is not in a cell.

/**
 * Filters a tap on the table to those on the headers/footer.
 * <p>
 * Note, indexPathForRowAtPoint: has a bug and returns valid indexPaths
 * for taps in the last section header and table footer. So an extra
 * check is made to ensure that the tap was, in fact, in the cell.
 */
- (IBAction) handleTap:(UITapGestureRecognizer*)tap {
    if (UIGestureRecognizerStateEnded == tap.state) {
        CGPoint point = [tap locationInView:tap.view];
        NSIndexPath* index = [self.table indexPathForRowAtPoint:point];
        UITableViewCell* cell;

        if (index) {
            cell = [self.table cellForRowAtIndexPath:index];
            point = [tap locationInView:cell];

            if (point.y < 0 || point.y >= cell.frame.size.height) {
                index = nil;
            }
        }

        if (!index) {
            [self.view performSelector:@selector(endEditing:)
                            withObject:@(YES) afterDelay:0];
        }
    }
}
like image 20
Matt Avatar answered Jan 21 '23 08:01

Matt