Add a TapGestureRecognizer to whole view except UICollectionView cells - ios

I'd like to add a TapGestureRecognizer to cover the whole screen of a UICollectionViewController except the UICollectionViewCell cells.
The closest I got was
-(void) viewDidLoad {
...
UITapGestureRecognizer *tapAnywhere = [[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(addBoard:)];
[self.collectionView addGestureRecognizer:tapAnywhere];
}
Problem: When I tap a cell the prepareForSegue method is not called. The UITapGestureRecognizer seems to cover the cell.
Which View in a UICollectionViewController is the right one to attach the GestureRecognizer to retain its default cell "tap to segue" functionatlity?

Implement Gesture Recognizer delegate method
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
if ([touch.view isKindOfClass:[UICollectionViewCell class]]) //It can work for any class you do not want to receive touch
{
return NO;
}
else
{
return YES;
}
}

Related

Detecting touch inside UITableViewCell subview

I am unclear where I should add the UIGestureRecognizer code to corresponding subviews of a UITableViewCell. I have read all the related questions I could find. Right now my cells and cell's subviewsare generated inside of cellForRowAtIndexPath. I have tried to add the Gesture inside of cellForRowAtIndexPath with this:
UITapGestureRecognizer* tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap:)];
[mySubview addGestureRecognizer:tapGesture];
tapGesture.cancelsTouchesInView = YES;
tapGesture.delegate = self;
However, this detects nothing. To verify my UIGesture recognizer is working I have used the above code on the tableView itself, and it does register touches as expected. Furthermore, when the tableView has the above gesture attached the below code is also being called as expected:
-(BOOL) gestureRecognizer:(UITapGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
NSLog(#"shouldRevceiveTouch");
return YES;
}
- (BOOL)gestureRecognizer:(UITapGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UITapGestureRecognizer *)otherGestureRecognizer
{
NSLog(#"simultaneously");
return YES;
}
I have tried to remove the GestureRecognizer from the tableView and inside of cellForRowAtIndexPath I have tried to attach the GestureRecognizer to the cell itself, any of its subviews, nothing else gets a touch detected. (None of the above code is triggered)
Clearly I am adding the GestureRecognizer incorrectly. Where/When would be an appropriate location/time to add the GestureRecognizer?
Thank you.
I've done similar thing, but it was UILongPressGestureRecognizer. I think there is no big difference (because all touches are received by UITableView). I've added gesture recognizer in controllers viewDidLoad method (NOT IN cell).
- (void) tableViewLongPress:(UILongPressGestureRecognizer *)gestureRecognizer {
CGPoint p = [gestureRecognizer locationInView:self.messageTableView];
NSIndexPath *indexPath = [self.messageTableView indexPathForRowAtPoint:p];
if (indexPath == nil)
NSLog(#"long press on table view but not on a row");
else {
UITableViewCell *cell = [self.messageTableView cellForRowAtIndexPath:indexPath];
CGPoint pointInCell = [cell convertPoint:p fromView:self.messageTableView];
}
}
You can change Long press to regular one and try it yourself
I needed to detect touches on different subviews inside my cell. also handling iOS 9's UITableViewCellContentView.
First I overrided touchesBegan inside the my custom UITableViewCell
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint point = [touch locationInView:touch.view];
// Imagine I have 2 labels inside my cell
CGPoint convertedPoint = [self.firstLabel convertPoint:point fromView:touch.view];
if ([self.firstLabel pointInside:convertedPoint withEvent:nil]) {
// Touched first label
return;
}
convertedPoint = [self.secondLabel convertPoint:point fromView:touch.view];
if ([self.secondLabel pointInside:convertedPoint withEvent:nil]) {
// Touched second label
return;
}
// no labels touched, call super which will call didSelectRowAtIndexPath
[super touchesBegan:touches withEvent:event];
}
And to fix support in iOS 9 we should override awakeFromNib or just disable the cell user intercations somehwere else if cell is not in Storyboard / xib:
- (void)awakeFromNib {
// Initialization code
self.contentView.userInteractionEnabled = NO;
}
of course we shouldn't forget to set our label user interactions enabled.
Not sure exactly what you are trying to do. If you just want to detect if the user taps on a cell within the table then you don't need to implement a gesture recognizer. Just implement the delegate method below to detect when a row from the table has been selected then process the elements of the row such as getting the subview, etc.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Do all my cool tap related stuff here for example, get the row that was tapped:
UITableViewCell *cell= [tableView cellForRowAtIndexPath:indexPath];
// get your subview (assume its a UIImageView) from cell - one way to do it below
UIImageView photo = (UIImageView *)[cell.contentView viewWithTag:PHOTO_TAG];
}
If you describe your problem a little further then perhaps I can offer additional suggestions.

touchesBegan not working on UITableView

I have UITableView over the full screen. What I would like to know is to find the location where I clicked a cell.
What I want to do is to show the copy option when any cell is clicked.
For that I tried
- (void) touchesEnded:(NSSet *)touches
withEvent:(UIEvent *)event {
//some code
}
but this method is not getting called.
Any idea how can I find user touch over UITableView
if you just want to show Copy option when any cell is clicked then you have to go for
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
method of tableView. there is no need to use Touch event.
Using this method you can get the copy of selected cell using indexPath.
Edit
To get the position of selected cell you can use rectForRowAtIndexPath method
CGRect rectInTableView = [tableView rectForRowAtIndexPath:indexPath];
Edit2
CGRect rectInSuperview = [tableView convertRect:rectInTableView toView:[tableView superview]];
You cannot detect the touch location on a UITableView. For detecting there are two options for you.
1. Either subclass your `UITableView`
OR
2. Add a `UIPanGesture` explicitly in the view.
UITableView inherits the property of pan gesture and UIScrollView by default. Hence by subclassing it you can override the gesture methods and detect UITouchEvents and on the basis of your location you can show the copy option. If you will add a UIPanGesture on your UITableView then you have to add this method in your UIViewController and detect the UIPanGesture touch events.
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
and your UIPanGesture selector method as below:
-(void) slideView: (UIPanGestureRecognizer *) recognizer {
switch (recognizer.state) {
case UIGestureRecognizerStateBegan:
CGPoint touchLocation =[recognizer locationInView:self.yourTableViewReference];
//Your Rest Of The Code.
break;
case UIGestureRecognizerStateChanged:
break;
case UIGestureRecognizerStateEnded:
break;
default:
break;
}
}
Hope it helps you.
In case of UIScrollview or UITableView, touches methods does not get triggered. Please read more on Responder Chain.
To get the required output you have to sub class UITableView and UITableCellView and override [hitTest:withEvent:] and [pointInside:withEvent:] to get the CGPoint in the respective view.
You can read more here.
Create custom uitableview with hidden copy button and implement the delegate method didSelectRowAtIndexPath and then on the basis of row selection unhide the copy button.

UIView "stealing" touch from UITableView

I have an autocomplete table that I create the frame for when the user types anything in a search bar button item.
The problem is, I want to be able to dismiss the tableview when the user touches outside of it or the table, so naturally I added a tap touch recogniser to the main view.
Problem is, the view "hijacks" the touches from the table, so when you try to touch the table it dismisses it (which makes perfect sense as the table is a subview of that UIView)
Anyone have any smart solutions?
(My initial hunch is to put the UITableView in a new window on top of the view's window, but I would prefer if there was something more elegant)
Maybe you can try
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
if ([touch.view.superview.superview isKindOfClass:[UITableView class]]) return NO;
else return YES;
}
//Init recognizer
UIGestureRecognizer *gestureRecognizer;
gestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap:)];
[gestureRecognizer setCancelsTouchesInView:NO];
[self.view addGestureRecognizer:gestureRecognizer];
self.tapRecognizer = (UITapGestureRecognizer *)gestureRecognizer;
gestureRecognizer.delegate = self;
[gestureRecognizer release];
and put some stuff for your gesture in
- (void)handleTap:(UITapGestureRecognizer *)recognizer {
}
Hope it helps :)
may be you can try
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;
methods for table view selection of row user will feel that user selected the value
and for
and for outside the table use uiviews touch begin methods and check for rect
CGRectContainsPoint(<#CGRect rect#>, <#CGPoint point#>)

Why does the delegate call didSelectRowAtIndexPath when I tap an element that has its own handler?

I have a situation where elements in a UITableViewCell subclass have either tap gesture recognizers or UIButtons.
When I tap these, not only do its handlers get called, but so does the didSelectRowAtIndexPath: delegate method.
Now, ideally, if the user doesn't tap any of those elements, this should be passed on to the view underneath (i.e. the tableview or table cell) and be processed.
Why aren't these elements swallowing the touches and preventing the TableView delegate method from firing?
You need to override the following function..
-(BOOL) gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
Here you can check if the touch was initiated from a control of your choice and if that is the case you need to return NO to prevent default action from happening here .. and return YES if the earlier defined handler is to be called
-(BOOL) gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{
if(touch.view == self.playButton ) {
return NO;
}
return YES;
}
I "hacked" a solution which puts a tapGesture recognizer on the contentView of my UITableViewCell subclasses, whose delegate is my custom UITableViewController subclass. I use an empty didSelectRowAtIndexPath: implementation. The delegate method is
- (void)didSelectTableCell:(MyTableViewCell*)aCell;
And that did the job. Just need to remember when the cell is created to make sure I set the delegate.
Inelegant, but works fine.

How to call didSelectRowAtIndexPath method with UITapGestureRecognizer?

I've searched for hours on Google and Stackoverflow, tried them but no luck.
I have a UITableView tblDepartment and a UISearchBar studentSearch above it.
I add a UITapGestureRecognizer to dismiss the keyboard from studentSearch textbox when users tap outside the search box:
UITapGestureRecognizer *gestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(hideKeyboard)];
[self.tblDepartment addGestureRecognizer:gestureRecognizer];
- (void)hideKeyboard
{
[studentSearch resignFirstResponder];
}
After that, the method didSelectRowAtIndexPath:(NSIndexPath *)indexPath isn't called anymore when I select row in tblDepartment. I know gestureRecognizer is the reason.
So, how can I hide the keyboard and still allow user to select row?
I tried this code but it didn't work:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
if ([touch.view isDescendantOfView:tblDepartment]) {
return NO;
}
return YES;
}
Set the gesture recognizer cancelsTouchesInView property to NO, it's YES by default, it prevents touches from reaching the underlying views if the GR recognizes its gesture.

Resources