I have a couple of UIScrollViews in my view controller. I want to overlay a view that captures a 2 finger swipe via UIPanGestureRecognizer which will not record the UIScrollView swipe gestures.
When I put a transparent view over my content with a 2 finger pan gesture, my taps and 1 finger swipes are not detected. I tried overwriting the pointInside: method to return NO
but then it doesn't record my 2 finger swipe.
The effect is similar to the 4 finger swipe to change apps.
You don't need an overlay view.
First implement UIPanGestureRecognizer that will handle 2 finger pan and assign it to your view that contains UIScrollViews
UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc]
initWithTarget:self
action:#selector(handlePan:)];
panGestureRecognizer.delegate = self;
panGestureRecognizer.minimumNumberOfTouches = 2;
panGestureRecognizer.maximumNumberOfTouches = 2;
[self.view addGestureRecognizer:panGestureRecognizer];
Use UIGestureRecognizerDelegate to handle 2 finger pan with UIScrollView pan gesture
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
And finally you are able to handle 2 fingers pan
- (void)handlePan:(UIPanGestureRecognizer *)gestureRecognizer
{
NSLog(#"pan");
}
If you want to stop scrolling UIScrollView when two finger pan is detected you can disable and enable UIScrollView pan recognizers
- (void)handlePan:(UIPanGestureRecognizer *)gestureRecognizer
{
if(gestureRecognizer.state == UIGestureRecognizerStateBegan)
{
_scrollView.panGestureRecognizer.enabled = NO;
}
if(gestureRecognizer.state == UIGestureRecognizerStateEnded)
{
_scrollView.panGestureRecognizer.enabled = YES;
}
NSLog(#"pan");
}
If you don't really need the overlay you can solve this with just gesture recognizers. I wrote this up as a test:
- (void)viewDidLoad {
[super viewDidLoad];
self.scrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds];
_scrollView.contentSize = CGSizeMake(self.view.bounds.size.width * 2, self.view.bounds.size.height);
UIView *green = [[UIView alloc] initWithFrame:self.view.bounds];
[green setBackgroundColor:[UIColor greenColor]];
UIView *blue = [[UIView alloc] initWithFrame:CGRectOffset(self.view.bounds, self.view.bounds.size.width, 0)];
[blue setBackgroundColor:[UIColor blueColor]];
[_scrollView addSubview:green];
[_scrollView addSubview:blue];
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(twoFingerPan:)];
[pan setMinimumNumberOfTouches:2];
[pan setMaximumNumberOfTouches:2];
[pan setDelaysTouchesBegan:YES];
[_scrollView addGestureRecognizer:pan];
[self.view addSubview:_scrollView];
}
- (void)twoFingerPan:(UIPanGestureRecognizer *)gesture {
switch (gesture.state) {
case UIGestureRecognizerStateBegan:
self.scrollView.scrollEnabled = NO;
break;
case UIGestureRecognizerStateCancelled:
case UIGestureRecognizerStateEnded:
case UIGestureRecognizerStateFailed:
self.scrollView.scrollEnabled = YES;
break;
default:
break;
}
NSLog(#"2 Fingers!");
}
I get the twoFingerPan: call back for when 2 fingers are used. The scroll view's panGestureRecognizer is still working at that point so I disable scrolling on the scroll view to handle the 2 finger pan. I've found this method work's pretty well. One sort of wonky thing is if the scroll view is decelerating the 2 finger gesture recognizer isn't called. Hope that helps!
Related
I am using two swipe gestures inside scrollview so i could be able to change photos and zoom in , swiping left works normally and smoothly while swiping right does not work properly , after many swipes it could be triggered once maybe,i am using this code:
//image zoom
self.scrollView.minimumZoomScale=1.0;
self.scrollView.maximumZoomScale=6.0;
self.scrollView.contentSize=CGSizeMake(1280, 960);
self.scrollView.delegate=self;
self.scrollView.clipsToBounds = YES;
self.scrollView.scrollEnabled = false;
UISwipeGestureRecognizer * swipeleft=[[UISwipeGestureRecognizer alloc]initWithTarget:self action:#selector(swipeleft:)];
swipeleft.direction=UISwipeGestureRecognizerDirectionLeft;
[self.view addGestureRecognizer:swipeleft];
UISwipeGestureRecognizer * swiperight=[[UISwipeGestureRecognizer alloc]initWithTarget:self action:#selector(swiperight:)];
swiperight.direction=UISwipeGestureRecognizerDirectionRight;
[self.view addGestureRecognizer:swiperight];
And these two methods to handle the swipe
-(void)swipeleft:(UISwipeGestureRecognizer*)gestureRecognizer
{
NSLog(#"LEFTtttttt");
if(index > 0 && index < _photoCount){
index = index - 1;
[self loadImage];
}
}
-(void)swiperight:(UISwipeGestureRecognizer*)gestureRecognizer
{
NSLog(#"RIGHTttttt");
if(index >= 0 && index < _photoCount - 1){
index = index + 1;
[self loadImage];
}
}
I searched for a solution of course and tried different codes but it did not work , i added
[self.scrollView.panGestureRecognizer requireGestureRecognizerToFail:swiperight];
And this one too,
-(void)scrollViewDidZoom:(UIScrollView *)scrollView {
if (scrollView.zoomScale!=1.0) {
// Zooming, enable scrolling
scrollView.scrollEnabled = TRUE;
} else {
// Not zoomed, disable scrolling so gestures get used instead
scrollView.scrollEnabled = FALSE;
}
}
The only difference is when i added the latter code swiping right worked normally when i zoomed in when it should work in the opposite case.
Did i miss something in my code?
Thanks.
Please add swipe gesture to the scrollView not on self.view to make it works good when user swipe on scrollView. like below
UISwipeGestureRecognizer * swipeleft=[[UISwipeGestureRecognizer alloc]initWithTarget:self action:#selector(swipeleft:)];
swipeleft.direction=UISwipeGestureRecognizerDirectionLeft;
[self.scrollView addGestureRecognizer:swipeleft];
UISwipeGestureRecognizer * swiperight=[[UISwipeGestureRecognizer alloc]initWithTarget:self action:#selector(swiperight:)];
swiperight.direction=UISwipeGestureRecognizerDirectionRight;
[self.scrollView addGestureRecognizer:swiperight];
this might be happening because scroll view is rendering above self.view.
I have leftMenu appears when swipe to right, and I want my tab changes when swipe to left whole page.
Their UIPanGestureRecognizer intercepts, I have tried disabling bouncing of UIScrollView of UIPageViewController, tried requireGestureRecognizerToFail:
But I could not make it both of them works. What I want is when it is swiped to to right side menu appears. When it is swiped to left if sidemenu is open it will close, if not, page will change.
My sample code is as follows;
for(UIScrollView *view in self.pageViewController.view.subviews)
{
if ([view isKindOfClass:[UIScrollView class]])
{
UIScrollView *scrollView = (UIScrollView *)view;
AppDelegate *appDel = [UIApplication sharedApplication].delegate;
UIPanGestureRecognizer* panGestureRecognizer = scrollView.panGestureRecognizer;
[panGestureRecognizer addTarget:self action:#selector(move:)];
[panGestureRecognizer requireGestureRecognizerToFail:appDel.slidingViewController.panGesture];
}
}
Thanks in advance
Something like the following should work (warning: untested!)
- (void)viewDidLoad
{
[super viewDidLoad];
UIPanGestureRecognizer *panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(panRecognized:)];
panRecognizer.delegate = self;
for (UIGestureRecognizer *recognizer in self.pageViewController.gestureRecognizers)
[recognizer requireGestureRecognizerToFail:panRecognizer];
}
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)recogniser
{
if (self.leftMenuShowing)
return YES;
else
{
UIPanGestureRecognizer *panRecognizer = (id)recognizer;
CGPoint translation = [panRecognizer translationInView:self.view];
return (translation.x > 0);
}
}
- (void)panRecognized:(UIPanGestureRecognizer *panRecognizer)
{
if (state == UIGestureRecognizerStateBegan || state == UIGestureRecognizerStateChanged)
{
// if pan is to left, move leftMenu offscreen
// if pan is to right, move leftMenu onscreen
}
else
{
// if leftMenu is mostly onscreen, animate completely onscreen
// if leftMenu is mostly offscreen, animate completely offscreen
}
}
I have a custom implementation of UITableViewCell.
The UITableViewCell can be swiped to the left or the right.
I use a UIPanGestureRecognizer for the same.
UIGestureRecognizer* recognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePan:)];
recognizer.delegate = self;
[self addGestureRecognizer:recognizer];
}
#pragma mark - horizontal pan gesture methods
-(BOOL)gestureRecognizerShouldBegin:(UIPanGestureRecognizer *)gestureRecognizer {
CGPoint translation = [gestureRecognizer translationInView:[self superview]];
// Check for horizontal gesture
if (fabsf(translation.x) > fabsf(translation.y)) {
return YES;
}
return NO;
}
-(void)handlePan:(UIPanGestureRecognizer *)recognizer {
if (recognizer.state == UIGestureRecognizerStateBegan) {
// if the gesture has just started, record the current centre location
// SOME CODE
}
if (recognizer.state == UIGestureRecognizerStateChanged) {
// translate the center
//SOME CODE
}
if (recognizer.state == UIGestureRecognizerStateEnded) {
// the frame this cell would have had before being dragged
//SOME CODE
}
}
Now I want to be able to support two finger swipe on on the entire screen such that even if a two finger swipe is done on a UITableViewCell, the above code is not triggered.
How can I achieve this ?
If you set the maximumNumberOfTouches on your gesture recognizer to 1, it will no longer accept multi-finger swipes:
UIGestureRecognizer* recognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePan:)];
recognizer.maximumNumberOfTouches = 1;
I have a cutsom UITableViewCell implemention.
I have registered this subclass of UITableViewCell for a UIPanGestureRecognizer which i use to swiping the cells to the right or left.
// in the UITableViewCell subclass :
UIGestureRecognizer* recognizer =
[[UIPanGestureRecognizer alloc] initWithTarget:
self
action:#selector(handlePan:)];
recognizer.delegate = self;
[self addGestureRecognizer:recognizer];
recognizer.cancelsTouchesInView = NO;
Now I want to to present a view controller when the user does a two finger swipe "up"
on the screen.
So, I added a UISwipeGestureRecognizer to the tableview.
// code in the view controller containing the tableview reference.
UISwipeGestureRecognizer *swipe = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(handleViewsSwipe:)];
[swipe setDirection:UISwipeGestureRecognizerDirectionUp];
[swipe setDelaysTouchesBegan:NO];
[[self tableView ]addGestureRecognizer:swipe];
swipe.cancelsTouchesInView= YES;
[swipe setNumberOfTouchesRequired:2];
swipe.delegate = self;
self.tableView.multipleTouchEnabled = YES;
But when I do a two finger swipe on the screen, the pan gesture gets triggered .
How can I solve this ?
As sooper's says, setting the maximumNumberOfTouches = 1 will probably work.
For others trying to deal with 2 gestureRecognizers at the same time that are both 1 touch gestures I found that making sure to set this delegate to yes
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
return YES;
}
and then in the gesture recognizer action you can check for a certain translation or whatever you need and cancel one of the gesture recognizers.
Such as:
- (void)panSwipeRecognizer:(UIPanGestureRecognizer*)panRecognizer
{
CGPoint translation = [panRecognizer translationInView:self.superview];
if(panRecognizer.state == UIGestureRecognizerStateBegan)
{
if(fabsf(translation.x) < fabsf(translation.y))
{
//deactivate horizontal gesture recognizer
panRecognizer.enabled = NO;
panRecognizer.enabled = YES;
}
else //if(fabsf(translation.x) > fabsf(translation.y))
{
//deactivate vertical gesture recognizer
otherGestureRecognizer.enabled = NO;
otherGestureRecognizer.enabled = YES;
}
}
//other statements like stateChanged and stateBegan
}
I have the following problem with a UIPanGestureRecognizer inside a UIScrollView:
UIScrollView *sv = [[UIScrollView alloc] initWithFrame:CGRectMake(200, 200, 200, 200)];
sv.contentSize = CGSizeMake(200, 100 *100);
for (int i = 0; i < 100; i++) {
UIView *newView = [[UIView alloc] initWithFrame:CGRectMake(0, i * 100, 200, 100)];
UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(panTile:)];
[panGesture setDelegate:self];
[panGesture setEnabled:FALSE];
[newView addGestureRecognizer:panGesture];
UILongPressGestureRecognizer *longPressRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(longPress:)];
[longPressRecognizer setDelegate:self];
[newView addGestureRecognizer:longPressRecognizer];
[sv addSubview:newView];
}
The complete scroll view is filled with small tiles, each of them implements a pan gesture in order to make them draggable. The problem is that - doing so - prevents the scroll view from scrolling. Dragging of the tiles instead works fine. When I disable the tiles pan gestures, scrolling works perfectly. The tiles pan gesture somewhat hides the scroll views own pan gesture. My idea was to disable the tiles pan gesture from the beginning. The gesture is enabled once the user does a long press on the tiles. The problem is that the user has to lift the finger and then touch the tile again to drag it. When the drag is finished, I enable the long press and disable the pan gesture again. So longPress: looks as follows:
- (void)longPress:(UILongPressGestureRecognizer *) gestureRecognizer {
for (UIGestureRecognizer *r in gestureRecognizer.view.gestureRecognizers) {
if ([r isKindOfClass:[UIPanGestureRecognizer class]]) {
[r setEnabled:TRUE];
}
}
//pan gesture should take over here...
}
Is there any possibility to glue the long press and the pan gestures together so that the user doesn't have to lift the finger? Or maybe another solution?