Conflicting UITapGestureRecognizer in UIView and for UITableView - iphone

I have a UIView in which I added a UITapGestureRecognizer. Inside that view I also have a subview in which is basically some kind of a UITableView. The question is that why doesn't the UITableView recognizes the tap on a row, instead it goes to the tap gesture recognizer's handler all the time. Why is this, and how do I solve this? If I set the number of taps to 2, then it works fine. Any idea on how to solve this? Basically it doesn't call the didSelectRowAtIndexPath.

Set cancelsTouchesInView of your recognizer to NO. Otherwise, it "consumes" the touch for itself, and does not pass it on to the table view. That's why the selection event never happens.

If you want both your UITableView and your UITapGestureRecognizer to receive touch events, then yes the cancelsTouchesInView = NO will work. If you want the tap gesture recognizer not to receive the touch events meant for the table view it is slightly less easy but very do-able.
Basically when you are creating your gesture recognizer you set self as its delegate. Then you implement the gestureRecognizer:shouldReceiveTouch: delegate method. A basic implementation might look like this.
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{
UITableView *tableView = self.tableView;
CGPoint touchPoint = [touch locationInView:tableView];
return ![tableView hitTest:touchPoint withEvent:nil];
}
Essentially this method (as implemented) asks the tableView if this touch's location falls within the tableView's jurisdiction, and if it does, it will block the gesture recognizer from receiving the touch...allowing the tableView to receive the touch.

Related

How can I tell when the user taps or drags a control without breaking the control?

Here's my situation: I've got a UISearchBar and a UITableView in a Navigation Controller. When the user taps in the search bar, the keyboard pops so the user can type a search string, as shown here:
But the keyboard obscures the list, and so I want to make it so that if the user taps the table, the keyboard goes away, and I'm having a surprising amount of difficulty.
If I use a TapGestureRecognizer, I can tell if the user taps anywhere, but then when they tap on the table, the normal table events don't work.
I tried something else (can't remember what now), and I got the event if the user taps the table, but not tap and drag, which is common, and so that's not quite enough.
What I want is something like a TouchDownInside event on the table itself, so I can dismiss the keyboard when the table gets user input of any kind, but this doesn't seem to exist. What's the best way to do this?
An easy solution would be to catch when the UITableView is scrolling, as the UITableView extends the UIScrollView you can look for the scrollViewDidScroll: and make the search bar resign the first responder to make the keyboard go away, same can be done with scrollViewWillBeginDragging: method
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
[searchBar resignFirstResponder];
}
Implement this on you UITableViewDelegate as it behaves as the delegate of your UITableView
More information at
http://developer.apple.com/library/ios/documentation/uikit/reference/uiscrollviewdelegate_protocol/
If I use a TapGestureRecognizer, I can tell if the user taps anywhere, but then when they tap on the table, the normal table events don't work.
You could try and set cancelsTouchesInView to NO for your tap gesture recogniser:
tapGestureRecognizer.cancelsTouchesInView = NO;
This should allow the touches to be forwarded to the view even when the gesture is recognized.
You might also need to define a delegate for your gesture recognizer and the following method:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
This is assuming that the table also manages its gestures through a gesture recognizer and thus to allow that both can work simultaneously.

Touch on UIButton in a UITableViewCell cannot scroll the table

I need to have a UIButton inside a UITableViewCell, everything works fine except when I touch inside the button and move my finger, the table does not scroll.
I tried to subclass UIButton and in touchesMoved: method send the same message to self.nextResponder, or call touchesCancelled and hope it will pass the touch event to next responder, they both do not work.
Is there any good way to solve this problem? Currently I am adding a UIView on top of the UITableViewCell, and detecting touch events manually, passing result to the button or the rest of the cell respectively, but this is a little bit dirty.
What you can try is setting the delaysContentTouches property of the UITableView to YES.
Additionally you can set the canCancelContentTouches to YES.
If the value of this property is NO, the scroll view does not scroll
regardless of finger movement once the content view starts tracking.
Source: UIScrollView Class Reference
Try:
_yourTableView.delaysContentTouches = YES;
_yourTableView.canCancelContentTouches = YES;
The best way is to make a custom cell and do your work neatly.
Check the Apple docs

UITableview didSelectRowAtIndexPath supercedes double tap gesture by UITapGestureRecognizer

I've set up a UITableView with a double-tap UITapGestureRecognizer. But attempts to double-tap a cell by the user just launches didSelectRowAtIndexPath twice. Are these two supposed to work together?
(i'm aware i could use a single tap gesture recognizer in place of the built-in behavior of didSelectRowAtIndexPath, but here's the problem with that: the cell also has a button that I can't press anymore when i add the single tap gesture recognizer. Also, I've seen examples on SO of users building double tap functionality into didSelectRowAtIndexPath, but isn't that a bit too much of a hack?)
More info about Kris' answer:
cancelsTouchesInView
delaysTouchesBegan
delaysTouchesEnded
http://developer.apple.com/library/ios/#documentation/uikit/reference/UIGestureRecognizer_Class/Reference/Reference.html
In my case I had a problem when adding 2 tap gestures to a UIImageView that was on a custom UITableViewCell. What happened was the didSelectRowAtIndexPath: was called when you tap/double tap on the UIImageView. When I had only one tap gesture, the didSelectRowAtIndexPath: was not called (and for me, that was the right behavior.
To prevent the didSelectRowAtIndexPath: from being called when using two tap gestures (single/double tap), I added this code to the first tap gesture (the single one):
tapGesture.cancelsTouchesInView = YES;
tapGesture.delaysTouchesBegan = YES;
After this change, a tap/double on the UIImageView (on top of the custom cell) did not trigger the didSelectRowAtIndexPath:
Looks like I can get didSelectRowAtIndexPath and the double tap gesture recognizer to play together nicely using the delaysTouchesBegan and cancelsTouchesInView properties of the gesture recognizer.
The other option described by #MSgambel seems to work equally well.
You can use a single-tap gesture recognizer in place of didSelectRowAtIndexPath, even if there is a button in the cell. You just need to check if the touch location is inside the UIButton's view or not in order to handle both cases. Hope that Helps!
I tested the "delayTouchesBegan" method with double taps but I found that the single tap received by the table is then delayed, making the table interaction less responsive to the user, and perhaps annoying.
My solution is a bit pedestrian but I use a timer to detect taps in method didSelectRowAtIndexPath. I record the tap count of "1" for the first tap, and if the user does not tap again with 0.2 seconds it displays the item selected. If the user tapped within 0.2 seconds for tap count "2" then I display another item (an action sheet). I reset the tap count each time.
This method uses more code, but provides quick response from the interface and the user does not need to know what is happening behind the scenes - just that the UI is responsive.

UIButton touch event falls through to underlying view

I have created a small UIView which contains two UIButtons. The view responds to UITapGesture events. The Buttons are supposed to respond to TouchUpInside, however when I tap the buttons the responder is the underlying view and the tap gesture selector is triggered. Looking for advice or suggestions.
You can modify the method that responds to the tap gesture in the orange view:
-(void) handleTapFrom:(UITapGestureRecognizer*) recognizer {
CGPoint location = [recognizer locationInView:orangeView];
UIView *hitView = [orangeView hitTest:location withEvent:nil];
if ([hitView isKindOfClass:[UIButton class]]) {
return;
}
//code that handle orange view tap
...
}
This way if you touch a UIButton, the tap will be ignored by the underlying view.
The right answer (which prevents the tabrecognizer from highjacking any taps and doesn't need you to implement a delegate etc) is found here. I got a lead to this answer via this post.
In short use:
tapRecognizer.cancelsTouchesInView = NO;
"Which prevents the tap recognizer to be the only one to catch all the
taps"
Each UIView has an 'exclusiveTouch' property. If it's set to YES the view won't pass the touch down the responder chain. Try setting this property on your UIButtons and see if that makes a difference.
How are the views ordered in the view hierarchy? Also, are you creating the interface in IB? If you hook up the connections properly, this shouldn't be an issue at all...
The problem with this design is that it's similar to embedding one button into another. While you may have a valid reason to do it, you should be more careful with the event flow.
What happens is the gesture recognizer intercepting touches before they reach subviews (the two buttons in your case). You should implement UIGestureRecognizerDelegate protocol, namely, gestureRecognizer:shouldReceiveTouch: method, and return NO if the touch is inside any of the two buttons. That will prevent the orange view from usurping touches intended for the buttons and the buttons will start to work as expected.
Check out this question.
uibutton-inside-a-view-that-has-a-uitapgesturerecognizer

fire event when touch up inside uiview and outside button

I have a view overlayed to the iPhone's camera view. In this view I have some uibuttons that are displayed in positions depending of values from accelerometer/compass.
I need to fire an event when the user touch up inside the view but he doesn't touch up inside the uibuttons, I mean, the opposite action of touch up inside the uibuttons.
Anyone knows a kind to do this?
Thanks!
If you implement the UIResponder touchesBegan:withEvent: method..
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
...on your view, you'll be able to tell when the user has pressed outside of the buttons.
See the UIResponder Class Reference for more information.
you may e.g. assign one event handler (IBAction) to UiVeiw and its UIButtons and then compare sender parameter:
-(IBAction) viewClicked :(id) sender {
if (id == myView) ...
}
I tried your options but it doesn't work. The view that I tried to fire the event was in a TabBar item in a camera view, so I think the responders chain doesn't work with it.
Finally, I solved the problem doing the overlay view the first responder with:[viewOverlay.view becomeFirstResponder];