I have a UIview that contains a table view.
I want the user to be able to select items in the table view and I want to be able to identify taps in the parent view, outside the table view.
If I add a Tab Gesture Recognizer to the UIView, the user is unable to select items in the table view.
How can accomplish this task?
In this case, You've to add gesture to your view and you can cancel gesture call back method by doing if touch happen on tableview as below.
1) set TAG to your tableview . self.tableView.tag = TAG;
2) Now cancel gesture if touch on tableview as below
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
id touchView= touch.view;
if ([touchView isKindOfClass:[UITableView class]] || [touchView isKindOfClass:[UITableViewCell class]] )
{
if ( ((UIView*)touchView).tag == TAG)
return NO;
}
return YES;
}
For those how uses #IBAction method. In Interface Builder Tap Gesture Recognizer in Attributes inspector just uncheck "Cancels touches in view".
Related
My UIScrollView inside a nested UITableView, in which UITableView has a left row pop-up menu of gestures, and now the two views of the gesture conflict. Now the question is: how to solve this problem without changing the UITableView ?
PS: I have been set UIScrollView ScrollEnabled=false
Your question is different but as you agree that your main problem is conflict between the gestures, that means you are not able to differentiate between gesture of two views,
To solved there are two ways, you need to receive gesture based on condition in gestureRecognizer delegate method, you can either check the class which received the gesture or by checking class or by tag
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldReceiveTouch:(UITouch *)touch
{
if([touch.view class] == [UITableView class]){
return NO;
} else if (touch.view.tag == 100) {
return NO;
} else if ([NSStringFromClass([touch.view class]) isEqualToString:#"UITableViewCellContentView"]) {
return NO;
}
return YES;
}
What this delegate would do is, it will call the gesture handler method only for the view which you want to handle gesture and also you can differentiate between the gesture recogniser.
Using UIPanGestureRecognizer & UITapGestureRecognizer on top-level UIView.
The setup in Interface Builder:
ViewController (our main view controller)
UIView (our main view and wired to our UIViewController
Our core UIView has a subview called a “Block” which is simply a UIView.
The Block view has 4 subviews (children) each being an instance of a UIButton.
The UIButton has its Touch Up Inside event wired to the the UIViewController.
The UIView (our main top-level UIView) has a UIPanGestureRecognizer and a UITapGestureRecognizer
Here is the scenario we are trying to accomplish (a.k.a.The behavior):
A user taps a button (a cell).
The button will change its stated from “normal” to “selected”. (This works fine and the code is simple)
With a selected item, a user can place their finger anywhere on the screen an move it up or down
The issue:
Need to know when panning stops.
The top UIView does not receive a gesture stated of ended.
The UIView does not receive a touchesEnded event.
How do you know when the user has lifted their finger? Suppose I start the panning when my finger is over a UIButton, while panning occurs, the UIButton eats the touches begin and end events. Therefore, you have no way of knowing when the user stopped moving their finger across the iPhone/iPad glass.
First implement UIGestureRecognizerDelegate in your view controller.
Then set the delegate on your gesture recognizers to self and implement the following method
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
Your target method should look like this:
-(void)gestureRegognized:(UIGestureRecognizer*)gestureRecognizer
{
if ([gestureRecognizer isMemberOfClass:[UIPanGestureRecognizer class]])
{
//check its state
if(gestureRecognizer.state==UIGestureRecognizerStateBegan)
{
// add your code here
}
else if(gestureRecognizer.state==UIGestureRecognizerStateEnded)
{
// pan gesture ended code goes here
}
}
else if([gestureRecognizer isMemberOfClass:[UITapGestureRecognizer class]])
{
if(gestureRecognizer.state!=UIGestureRecognizerStateFailed)
{
// tap gesture detected
}
}
}
I am using a tap gesture on my view which also has a table view as a subview. The table does scroll but when tapped, instead of calling didSelectRowAtIndexPath it calls the selector associated with the tap gesture. I can detect the tapped view by getting tap location.
I want to access didSelectRowAtIndexPath when tapped on table instead of the tap gesture selector. How do I achieve this?
Implement the tap gesture's UIGestureRecognizerDelegate , and prevent the gesture if touch is in the tableview.
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
CGPoint p = [gestureRecognizer locationInView:view] ;
if (CGRectContainsPoint(tableview.frame, p)) {
return NO ;
}
return YES ;
}
I have a view with a UIToolbar with a few UIBarButtonItems and a UITableView containing some UITextFields.
I would like to dismiss the keyboard for a textfield with a tap anywhere. Therefore I added a TapGestureRecognizer to the view. To avoid that the TapgestureRecognizer handles taps on the UIBarButtonItems I added the following method (delegate is set).
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
UIView *view = touch.view;
while (view) {
NSLog(#"Class of view: %#", NSStringFromClass([view class]));
view = view.superview;
}
// Disallow recognition of tap gestures in the toolbar
if ([touch.view isKindOfClass:[UIToolbar class]]) {
return NO;
}
if ([touch.view.superview isMemberOfClass:[UIToolbar class]]) {
return NO;
}
return YES;
}
A UIBarButtonItem is not a view itself, but it has UIToolbar as its superview. When I use the above method, the check for isKindOfClass:[UIToolbar class] does not seem to work for all taps on the toolbar. However the check for the superview with isMemberOfClass:[UIToolbar class] works.
I don't understand this. Maybe someone can explain this behavior?
You shouldn't rely on the view hierarchy around private view classes. It could change at any time.
A better approach is to add the gesture to the table view (or other appropriate view which represents the area you're interested in). Just be sure to enable and disable the gesture at appropriate times so as not to block the usual table operation.
I am hoping someone will be able to help me with a problem that is doing my head in at the moment!
Given the following view hierarchy
I want to be able to detect swipe gestures on my custom UITableViewCell.
I have subclassed the UIScrollView and have a hitTest:withEvent: method that checks whether I am touching the tableview cell (or its content) or not, in which case I set the following scroll view properties:
- (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
UIView* result = [super hitTest:point withEvent:event];
if ([result.superview isKindOfClass:[UITableViewCell class]] || [result.superview tag] == SUBVIEW_TAG)
{
self.canCancelContentTouches = NO;
self.delaysContentTouches = YES;
} else {
self.canCancelContentTouches = YES;
self.delaysContentTouches = NO;
}
return result;
}
I have also implemented:
- (BOOL)touchesShouldCancelInContentView:(UIView *)view
{
if (view.tag == SUBVIEW_TAG || [[view superview] isKindOfClass:[UITableViewCell class]])
return NO;
return YES;
}
And am returning NO in case the view being touched is the table view cell.
These methods are all getting called and performing their actions as expected, but I am still unable to stop the UIScrollView from "hogging" the swipe gesture.
The interesting thing is that if I include the UIView that contains the tableview and cell on both of the methods above (the one with SUBVIEW_TAG) it works perfectly so I am guessing it must be something to do with the fact that UITableView inherits from UIScrollView.
My main goal is to be able to swipe on the cell to reveal more options for the cell. A horizontal swipe anywhere else on that view would be captured by the scroll view and shift the content horizontally as per its normal behaviour.
Any ideas would be very much appreciated!
Thanks!
Rog
I had a similar problem with a swipe detect for a component inside a scrollview and I was able to resolve it with
[scrollView.panGestureRecognizer requireGestureRecognizerToFail:swipeGesture]
Where scrollView is the scroll view object that acts like container and swipeGesture is the component swipe gesture object inside scrollview.
So, you can define a swipe for the cell object like this (for right swipe in the example, custom it as you want)
UISwipeGestureRecognizer* rightSwipeRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(yourMethod)];
[rightSwipeRecognizer setDirection:UISwipeGestureRecognizerDirectionLeft];
[cell addGestureRecognizer:rightSwipeRecognizer];
and then do
[scrollView.panGestureRecognizer requireGestureRecognizerToFail:rightSwipeRecognizer]
The documentation of requireGestureRecognizerToFail says:
This method creates a relationship with another gesture recognizer
that delays the receiver’s transition out of
UIGestureRecognizerStatePossible. The state that the receiver
transitions to depends on what happens with otherGestureRecognizer:
If otherGestureRecognizer transitions to
UIGestureRecognizerStateFailed, the receiver transitions to its normal
next state.
if otherGestureRecognizer transitions to
UIGestureRecognizerStateRecognized or UIGestureRecognizerStateBegan,
the receiver transitions to UIGestureRecognizerStateFailed.
An example where this method might be called is when you want a
single-tap gesture require that a double-tap gesture fail.
Availability Available in iOS 3.2 and later.
Hope helps!
The solution is pretty simple. All you need to do is add UIScrollView inside you UITableViewCell. It will prevent "hogging" effect during swipe gesture.