I'm working on a custom swipe event for a UITableView that uses custom UITableViewCell subclass. I included the UIGestureRecognizerDelegate in my header, and have this in viewDidLoad:
UISwipeGestureRecognizer *swipeLeft = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(didSwipe:)];
swipeLeft.direction = UISwipeGestureRecognizerDirectionLeft;
swipeLeft.numberOfTouchesRequired = 1;
[self.tableView addGestureRecognizer:swipeLeft];
My swipeLeft method looks like so:
-(void)didSwipe:(UISwipeGestureRecognizer *)recognizer {
if (recognizer.state == UIGestureRecognizerStateEnded)
{
CGPoint swipeLocation = [recognizer locationInView:self.tableView];
NSIndexPath *swipedIndexPath = [self.tableView indexPathForRowAtPoint:swipeLocation];
NSDictionary *clip = [self.clips objectAtIndex:swipedIndexPath.row];
NSLog(#"Swiped!");
}
}
It's sort of working, but the swipe has to be incredibly precise. Like nearly impossibly precise.
I almost got it working by using a UIPanGestureRecognizer instead, but unfortunately it didn't play nice with the global side drawer component that uses a global pan gesture recognizer (ECSlidingViewController for those interested).
Is there any way around this? Any help would be appreciated, as I've been googling around and browsing SO for hours looking for a solution.
As pointed out by Kolin Krewinkel on Twitter, implementing these 2 delegate methods did the trick:
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
return YES;
}
I was having a similar problem using the ECSlidingViewController and swipe-to-delete on a UITableView (the top view controller in my case slides to the left to reveal the menu).
I fixed the problem by adding a delegate to the default panGesture property of my ECSlidingViewController like this to only pull in the menu if the swipe starts in the very right-hand edge of the screen:
-(BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
if ([gestureRecognizer locationInView:gestureRecognizer.view].x > ([UIScreen mainScreen].bounds.size.width - 60.0))
{
return YES;
}
return NO;
}
Related
I am adding two gesture on a view. One is tap gesture and second is swipe gesture. Whenever user tries to swipe the view , tap gesture method gets called (tap gesture gets detected). What could be the reason ? Tap gesture should get ignore in this case.
below is the code .
UISwipeGestureRecognizer *swipeGest = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:#selector(itemSwiped:)];
swipeGest.direction = UISwipeGestureRecognizerDirectionUp ;
[messageView addGestureRecognizer:swipeGest];
UITapGestureRecognizer *gest = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(itemSelected:)];
[messageView addGestureRecognizer:gest];
use this delegate method to avoid other gesture
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
if ([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]) {
[otherGestureRecognizer requireGestureRecognizerToFail:gestureRecognizer];
return false;
}else if([gestureRecognizer isKindOfClass:[UISwipeGestureRecognizer class]]){
[otherGestureRecognizer requireGestureRecognizerToFail:gestureRecognizer];
return false;
}
return true;
}
Because your using multiple gesture on same view. this may over hide one another property .For Ex: first gesture recognizer recognizes the gesture (tap in this case) it cancels all touch events. Therefore the remaining gesture recognizers will never finish recognition, and will never fire their events.
In order to add more than one UIGestureRecognizer onto the same view, you need to set the delegate property (UIGestureRecognizerDelegate)
yourGesture.delegate = self
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
if (gestureRecognizer isKindOfClass: [UISwipeGestureRecognizer class] || gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]) {
return YES
} else {
return NO
}
}
Try this;
UISwipeGestureRecognizer *swipeGest = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:#selector(itemSwiped:)];
swipeGest.direction = UISwipeGestureRecognizerDirectionUp ;
[messageView addGestureRecognizer:swipeGest];
UITapGestureRecognizer *gest = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(itemSelected:)];
gest.numberOfTapsRequired = 1;
[messageView addGestureRecognizer:gest];
-(void)itemSwiped:(UISwipeGestureRecognizer*)gesture
{
NSLog(#"swipe");
}
-(void)itemSelected:(UITapGestureRecognizer*)gesture
{
NSLog(#"tap");
}
Change single tap to doubletap gesture. so you can be sure user only double tap or swipe. It is good advice from me please follow it.
I've an UISplitViewController-Subclass with two UITableViewControllers (master & detail) in my Storyboard.
My BOSplitViewController is extended by a subview to draw in it.
_linienView = [[BODrawLinienView alloc] initWithFrame:self.view.bounds];
[self.view addSubview:_linienView];
In BODrawLinienView i've implemented my UIPanGestureRecognizer to Draw UIBezierPaths.
_panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(recognizedGesture:)];
_panRecognizer.minimumNumberOfTouches = 1;
_panRecognizer.maximumNumberOfTouches = 1;
_panRecognizer.delegate = self;
_panRecognizer.cancelsTouchesInView = NO;
[self addGestureRecognizer:_panRecognizer];
And the UIGesturerecognizerDelegate methods are implemented there, too.
#pragma mark - <UIGestureRecognizerDelegate>
- (BOOL)gestureRecognizerShouldBegin:(UIPanGestureRecognizer *)panGestureRecognizer {
CGPoint velocity = [panGestureRecognizer velocityInView:self];
return fabs(velocity.y) < fabs(velocity.x); // < >
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
The check for velocity in gestureRecognizerShouldBegin: allows me to avoid vertical events handled by the recognizer, who only should accept more or less horizontal events.
But all events avoided by this check aren't forwarded to the UITableViews in the SplitViewControlles ViewControllers.
EDIT: I've prepared a demo-project of this behavior on my Dropbox directory
Does anybody know how to realize forwarding of events to these Views?
Please help me with the below scenario.
Self.view (Swipe Gesture added here)
UicollectionView Object in subView (Default pan,swipe gestures are there)
Want to disable scrollview scrolling/failCollectionViewPan gesture for 2 finger swipe event
Means collection view will not scroll if 2 finger swipe executed
Another approach can be to disable Collection view scrolling while 2 fingers used. Over here, I want not to swipe collection view on 2 finger swipe.
I have implemented this code, but its slowing down scrolling.
[self.collectionView.panGestureRecognizer shouldBeRequiredToFailByGestureRecognizer:_swipeL];
Then I have implemented below codes
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{
return YES;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
return YES;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{
return YES;
}
Now both, Swipe and Scroll are working together.
Then, I have tried in a below manner, but still not got fixed.
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
if([gestureRecognizer isKindOfClass:[UISwipeGestureRecognizer class]]){
if(gestureRecognizer.numberOfTouches==2){
if( [[otherGestureRecognizer.view class] isSubclassOfClass:[UITableViewCell class]] ||
[NSStringFromClass([otherGestureRecognizer.view class]) isEqualToString:#"UITableViewCellScrollView"] ||
[NSStringFromClass([otherGestureRecognizer.view class]) isEqualToString:#"UITableViewWrapperView"] || [NSStringFromClass([otherGestureRecognizer.view class]) isEqualToString:#"UIScrollViewPanGestureRecognizer"] || [NSStringFromClass([otherGestureRecognizer.view class]) isEqualToString:#"UIScrollViewPagingSwipeGestureRecognizer"])
{
NSLog(#"Allow&Disable %#", [otherGestureRecognizer description]);
[gestureRecognizer requireGestureRecognizerToFail:otherGestureRecognizer];
return NO;
}
}
}
return YES;
}
Also I have implemented below 2 methods to fix it but fails.
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer NS_AVAILABLE_IOS(7_0);
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer NS_AVAILABLE_IOS(7_0);
Its is able to fix for ScrollView with such ways, but the same ways are not working for UICollectionView. Because of, colection view's default pan gesture, it cannot be able to modified. While tring this way, app crashed.
If you want to detect pan gesture, try something like this. But you need to recognise direction of pan gesture:
- (void)viewDidLoad
{
[super viewDidLoad];
UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(panGesture:)];
panGesture.delegate = self;
[self.yourCollectionView addGestureRecognizer:panGesture];
}
- (void)panGesture:(UIPanGestureRecognizer *)gesture;
{
if (gesture.numberOfTouches == 2) {
NSLog(#"Two fingers pan gesture");
}
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]
&& gestureRecognizer.numberOfTouches == 2) {
return NO;
}
return YES;
}
Your problem with UISwipeGestureRecognizer is, that it fires later than UIPanGesture, so in the shouldRecognizeSimultaneouslyWithGestureRecognizer delegate call, swipe gesture is always otherGestureRecognizer, and UIPanGesture is always gestureRecognizer, and in this function you can only disable otherGestureRecoginzer...
UPDATE:
Another solution: use another UIPanGestureRecognizer to disable scroll pan gesture:
- (void)viewDidLoad
{
[super viewDidLoad];
UISwipeGestureRecognizer *swipeGesture = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(swipeGesture:)];
swipeGesture.direction = UISwipeGestureRecognizerDirectionDown;
swipeGesture.delegate = self;
swipeGesture.numberOfTouchesRequired = 2;
UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:nil];
panGesture.delegate = self;
[self.tableView addGestureRecognizer:panGesture];
[self.tableView addGestureRecognizer:swipeGesture];
}
- (void)swipeGesture:(UIPanGestureRecognizer *)gesture;
{
NSLog(#"Two fingers swipe gesture");
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]
&& gestureRecognizer.numberOfTouches == 2) {
return NO;
}
return YES;
}
This answer might be late, but I did have a similar problem today and couldn't find a solution, until I figured something out myself. It's quite simple, actually.
Swift 5
We create a gesture
let gesture = UIPanGestureRecognizer(target: self, action: #selector(functionCall))
gesture.minimumNumberOfTouches = 2
gesture.maximumNumberOfTouches = 2
We then assign that gesture to the collectionView, effectively overwriting the scroll gesture on it.
collectionView.addGestureRecognizer(gesture) // simply overwrites the 2 finger gesture on the collectionView
It now is possible to scroll the collectionView with 1, 3 or more fingers, but the 2 finger pan gesture is blocked.
I've looked around on Stack for solutions to this and found quite a few results, but I'm still unable to fix my problem, mainly because I'm working on a CCNode opposed to some UIView.
Anyways, my CCButton works find without the UITapGestureRecognizer, but when I implement it, it overrides my button press. Obviously I would like to avoid this.
My code is below. My swipe gestures work perfectly, but the tap interferes with my button. I'm not sure if I'm adding the gesture recognizer to the wrong view, because - (BOOL)gesturerecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch never runs, which is where I would put in my exception. If anyone can give me some code/ideas on how to fix this it would be greatly appreciated, thanks!
-(void)didLoadFromCCB
UITapGestureRecognizer * gridTapped = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(gridTapped)];
[[[CCDirector sharedDirector] view] addGestureRecognizer:gridTapped];
//this works totally fine
UISwipeGestureRecognizer * swipeLeft= [[UISwipeGestureRecognizer alloc]initWithTarget:self action:#selector(swipeLeft)];
swipeLeft.direction = UISwipeGestureRecognizerDirectionLeft;
[[[CCDirector sharedDirector] view] addGestureRecognizer:swipeLeft];
}
-(void)gridTapped {
//this works!
CCLOG(#"Grid tapped");
}
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
//This never runs
CCLOG(#"gesture recognized");
if ((touch.view == mainMenuButton)) {
return NO;
}
return YES;
}
Try setting the UITapGestureRecognizer delegate:
gridTapped.delegate = self;
You'll need to declare the VC class as implementing <UITapGestureRecognizerDelegate>
I am using UIPageController to implement a page based navigation application. I would like to enable the swipe gestures only at the margin of the page and prevent that from the inner content view. The page has a margin of 20 pixels:
contentView.frame = CGRectInset(self.view.frame, 20, 20);
Upon recognising a gesture you should be able to retrieve the locationInView and then if this is an acceptable value proceed, otherwise not.
First add in the class interface.
#interface MyPageViewController :UIViewController<UIPageViewControllerDelegate, UIGestureRecognizerDelegate>
Then in viewDidLoad add
for (UIGestureRecognizer *recognizer in self.pageViewController.gestureRecognizers) {
recognizer.delegate = self;
}
Then implement shouldReceiveTouch method
#pragma mark - UIGestureRecognizer delegate methods
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{
CGPoint touchPoint = [touch locationInView:self.pageViewController.view];
CGRect innerRect = CGRectInset(self.pageViewController.view.frame, 40, 40);
if (CGRectContainsPoint(innerRect, touchPoint)) {
return NO;
}
return YES;
}