PROBLEM:
When the UIPanGestureRecognizer
is underlying a UIScrollView
- which unfortunately does also effect UIPageViewController
- the maximumNumberOfTouches
is not behaving as expected, the minimumNumberOfTouches
however always limits the lower end correctly.
When monitoring these parameters they report back correct values - they seem to do their job - it's just that UIScrollView
itself doesn't honor them and ignores their settings!
SOLUTION:
Set the minimumNumberOfTouches
to the desired value e.g. 1 and - very importantly - the maximumNumberOfTouches
to 2 !!!
myScrollView.panGestureRecognizer.minimumNumberOfTouches = 1;
myScrollView.panGestureRecognizer.maximumNumberOfTouches = 2;
Conform to the UIGestureRecognizerDelegate
protocol in your scrollView's @interface declaration. You don't have to set the panGestureRecognizer.delegate
for a UIScrollView
!!! The delegate is already set because UIScrollView
requires to be the delegate of its own pan/pinchGestureRecognizer
.
Then implement the UIGestureRecognizer
delegate method:
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
NSLog(@"%d", gestureRecognizer.numberOfTouches);
NSLog(@"%@", gestureRecognizer.description);
if (gestureRecognizer == self.panGestureRecognizer) {
if (gestureRecognizer.numberOfTouches > 1) {
return NO;
} else {
return YES;
}
} else {
return YES;
}
}
}
AN EVEN SAFER VERSION:
If you have a custom scrollView class and wanna be on the VERY safe side you can also add one more line of code to disambiguate against other scrollViews:
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
NSLog(@"%d", gestureRecognizer.numberOfTouches);
NSLog(@"%@", gestureRecognizer.description);
if ([gestureRecognizer.view isMemberOfClass:[MY_CustomcrollView class]]) {
if (gestureRecognizer == self.panGestureRecognizer) {
if (gestureRecognizer.numberOfTouches > 1) {
return NO;
} else {
return YES;
}
} else {
return YES;
}
} else {
return YES;
}
}
ADDITIONAL INFORMATION:
The NSLogs tell you the number of touches. If you set both the min and max to the same value (like 1 in the example above) the if-loop
would never be triggered... ;-)
That is why maximumNumberOfTouches
has to be at least minimumNumberOfTouches + 1
panGestureRecognizer.minimumNumberOfTouches = 1;
panGestureRecognizer.maximumNumberOfTouches = minimumNumberOfTouches + 1;
GEEK SECTION:
for (UIView *view in self.view.subviews) {
if ([view isKindOfClass:[UIScrollView class]]) {
NSLog(@"myPageViewController - SCROLLVIEW GESTURE RECOGNIZERS: %@", view.gestureRecognizers.description);
((UIPanGestureRecognizer *)view.gestureRecognizers[1]).minimumNumberOfTouches = 1;
((UIPanGestureRecognizer *)view.gestureRecognizers[1]).maximumNumberOfTouches = 2;
}
}
This is the way to access the underlying scrollView that is responsible for the paging of a UIPageViewController
. Put this code in the e.g. viewDidLoad:
of the UIPageViewController
(self).
If you don't have access to the scrollView at all - like for a UIPageViewController
in a dynamic UITableViewCell
where creation and cell reuse happens at runtime and no outlets can be set on its contentViews - put a category on UIScrollView
and override the delegate method there. But be careful! This effects every scrollView in your application - so do proper introspection (class-checking) like in my 'EVEN SAFER' example above... ;-)
SIDENOTE:
Unfortunately the same trick doesn't work with the pinchGestureRecognizer
on UIScrollView
because it doesn't expose a min/maxNumberOfTouches
property. When monitored it always reports 2 touches (which you obviously need to pinch) - so its internal min/maxNumberOfTouches
seem to have been set both to 2 - even if UIScrollView
isn't honoring its own settings and keeps happily pinching with any numbers of fingers (more than 2). So there is no way to restrict pinching to a limited amount of fingers...