In my iPhone app I have one messaging screen. I have added UITapGestureRecognizer
on the UIViewController
and also I have a UITableview
on
Assuming you want the tap gesture to work everywhere except over the tableview, you could subclass the tap gesture recognizer, creating a recognizer that will ignore any subviews included in an array of excludedViews
, preventing them from generating a successful gesture (thus passing it on to didSelectRowAtIndexPath
or whatever):
#import <UIKit/UIGestureRecognizerSubclass.h>
@interface MyTapGestureRecognizer : UITapGestureRecognizer
@property (nonatomic, strong) NSMutableArray *excludedViews;
@end
@implementation MyTapGestureRecognizer
@synthesize excludedViews = _excludedViews;
- (id)initWithTarget:(id)target action:(SEL)action
{
self = [super initWithTarget:target action:action];
if (self)
{
_excludedViews = [[NSMutableArray alloc] init];
}
return self;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesBegan:touches withEvent:event];
CGPoint location = [[touches anyObject] locationInView:self.view];
for (UIView *excludedView in self.excludedViews)
{
CGRect frame = [self.view convertRect:excludedView.frame fromView:excludedView.superview];
if (CGRectContainsPoint(frame, location))
self.state = UIGestureRecognizerStateFailed;
}
}
@end
And then, when you want to use it, just specify what controls you want to exclude:
MyTapGestureRecognizer *tap = [[MyTapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)];
[tap.excludedViews addObject:self.tableView];
[self.view addGestureRecognizer:tap];
You can use the TagGesture delegate:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
if ([touch.view isDescendantOfView:yourTableView]) {
return NO;
}
return YES;
}
Hope this helps.
While I prefer Matt Meyer's suggestion or my other suggestion of using a custom gesture recognizer, another solution, not involving custom gesture recognizers, would be to have your tap gesture recognizer identify whether you tapped on a cell in your tableview, and if so, manually invoke didSelectRowAtIndexPath
, e.g.:
- (void)handleTap:(UITapGestureRecognizer *)sender
{
CGPoint location = [sender locationInView:self.view];
if (CGRectContainsPoint([self.view convertRect:self.tableView.frame fromView:self.tableView.superview], location))
{
CGPoint locationInTableview = [self.tableView convertPoint:location fromView:self.view];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:locationInTableview];
if (indexPath)
[self tableView:self.tableView didSelectRowAtIndexPath:indexPath];
return;
}
// otherwise proceed with the rest of your tap handling logic
}
This is suboptimal because if you're doing anything sophisticated with your tableview (e.g. in cell editing, custom controls, etc.), you lose that behavior, but if you're just looking to receive the didSelectRowAtIndexPath
, then this might do the job. The other two approaches (separate views or the custom gesture recognizer) let you retain the full tableview functionality, but this could work if you just need something simple and you don't need the rest of the tableview's built-in capabilities.
An easier way to do this is to have two views: one containing the view that you want the tap gesture to be on, and one containing the tableview. You can attach the UITapGestureRecognizer to the view you want it to work on, and then it won't block your UITableView.