ScrollView collide with UItableview - ios

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.

Related

Adding a UIGestureRecognizer taking priority over all other interactions

When I tap on a UIButton, a UIView MyView appear from the bottom a cover a third of the screen. I would like that when I tap somewhere outside this view, it disappears.
I thought about adding another transparent UIView right under MyView and add a tab gesture on it with the dismiss function but I'm sure there is something cleaner than this.
So I thought about adding the tap gesture MyTapGesture to dismiss MyView on self.view of the UIViewController. The problem is that outside this view, I have other UIControls and gestures that capture also any touch at the same time than MyTapGesture.
How can I make MyTapGesture the priority gesture outside MyView and ignore all other gesture, taps, etc...?
You may have to use the gesture delegate methods to handle two tapGestureRecognizer activate the one you need depending on scenario
#pragma mark - UIGestureRecognizerDelegate methods
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
return YES;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
if ([tapGestureRecognizer1 isEqual:gestureRecognizer]) {
return [tapGestureRecognizer2 isEqual:otherGestureRecognizer];
}
if ([tapGestureRecognizer2 isEqual:gestureRecognizer]) {
return [tapGestureRecognizer1 isEqual:otherGestureRecognizer];
}
return NO;
}

Disable horizontal scrolling on UITableViewCell when UITableView is still scrolling vertically

I added a UIPanGestureRecognizer on my UITableViewCell subclass so that when the user swipes to the left, the buttons underneath are revealed (kind of like in the Mail app). I do this in awakeFromNib:
// Add a pan gesture recognizer to the pannable view.
UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(panScrollView:)];
panGesture.delegate = self;
[self.pannableView addGestureRecognizer:panGesture];
I want to allow the table view to scroll despite my custom gesture recognizer so I also have the following in the same UITableViewCell subclass:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
My problem now is I don't know how to allow only one gesture recognizer at a time, so that when the user is side-swiping, the table view doesn't scroll, and vice versa. Help?
What I've tried that doesn't work
Method 1
Implement UIGestureRecognizerDelegate in the table view's view controller and require the failure of any other gesture recognizer besides the vertical one which is for scrolling the table view.
Set the table view's panGestureRecognizer's delegate to the view controller that contains it (say it's ContainingViewController).
self.tableView.panGestureRecognizer.delegate = self;
Make ContainingViewController implement UIGestureRecognizerDelegate.
Implement shouldRequireFailureOfGestureRecognizer: and return YES (I'm assuming that the otherGestureRecognizer will be the horizontal panning gesture).
Error: 'UIScrollView's built-in pan gesture recognizer must have its scroll view as its delegate.'
Create a bool that allows your panGesture to action inside panScrollView
-(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
self.blockPanGesture = YES;
}
-(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
self.blockPanGesture = NO;
}
Then
-(void)panScrollView:(UIPanGestureRecognizer *)panGestureRecognizer
{
if(self.blockPanGesture == NO)
{
// do the stuff
}
}
If you are panning the entire tableview personally I'd put the gesture recogniser on the tableView itself... Otherwise there's also
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
// Return YES if you want the specified item to be editable.
return YES;
}
which handles all this for you... but I'm assuming there's a reason you don't want to use this. You can also build on this logic, for example you might want to tweak when the scrollView can drag as well, by putting similar checks in -(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView etc.
Using Magoo solution, I did something simillar which works for me. Hope it helps someone.
In Controller class
#pragma mark - UIScrollViewDelegate methods
-(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
ApplicationDelegate.blockPanGesture = YES;
}
-(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
ApplicationDelegate.blockPanGesture = NO;
}
Inside Cell:
- (IBAction)panGestureRecognizer:(UIPanGestureRecognizer *)panGesture
{
if(ApplicationDelegate.blockPanGesture)
return;
// do your stuff
}
Just FYI: have dragged UIPanGestureRecognizer in Cell's xib file and created above IBOutletAction |panGestureRecognizer:|
Have you tried setting the bounces property to NO?

Disable Touches on a view

Hi I have a UIView who's alpha is 0.7 and below it are some UITextFields. I don't want it to call touches events while keeping touches events. I tried using
[lightBoxView setUserInteractionEnabled:NO];
But now the UITextFields can become active or first responder. How can I disable it from calling touch events as well as not passing touches to others?
You also need to set the userInteractionEnabled = NO for all the subviews as well.
Try this,
[[lightBoxView subviews] makeObjectsPerformSelector:#selector(setUserInteractionEnabled:)
withObject:[NSNumber numberWithBool:NO]];
This will call setUserInteractionEnabled: on all direct subviews of lightBoxView and set it to NO. For a more complex subview hierarchy you will have to recursively loop through all the child views and disable the user interaction on each one. For this you can write a recursive method in a UIView category. For more details about this category method take a look at this answer.
Hope that helps!
You can remove those control from tough gesture delegate method.
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
if ([touch.view isKindOfClass:[UITextFiled class]])
{
return NO;
}
else
{
return YES;
}
}

Tap on UIBarButtonItem is not ignored by TapGestureRecognizer

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.

Detecting swipe gestures on UITableViewCell inside UIScrollView

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.

Resources