UIPageViewController Gesture recognizers
You can override
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldReceiveTouch:(UITouch *)touch
to better control when the PageViewController should receive the touch and not. Look at "Preventing Gesture Recognizers from Analyzing Touches" in Dev API Gesture Recognizers
My solution looks like this in the RootViewController for the UIPageViewController:
In viewDidLoad:
//EDITED Need to take care of all gestureRecogizers. Got a bug when only setting the delegate for Tap
for (UIGestureRecognizer *gR in self.view.gestureRecognizers) {
gR.delegate = self;
}
The override:
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
//Touch gestures below top bar should not make the page turn.
//EDITED Check for only Tap here instead.
if ([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]) {
CGPoint touchPoint = [touch locationInView:self.view];
if (touchPoint.y > 40) {
return NO;
}
else if (touchPoint.x > 50 && touchPoint.x < 430) {//Let the buttons in the middle of the top bar receive the touch
return NO;
}
}
return YES;
}
And don't forget to set the RootViewController as UIGestureRecognizerDelegate.
(FYI, I'm only in Landscape mode.)
EDIT - The above code translated into Swift 2:
In viewDidLoad:
for gr in self.view.gestureRecognizers! {
gr.delegate = self
}
Make the page view controller inherit UIGestureRecognizerDelegate
then add:
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
if let _ = gestureRecognizer as? UITapGestureRecognizer {
let touchPoint = touch .locationInView(self.view)
if (touchPoint.y > 40 ){
return false
}else{
return true
}
}
return true
}
Access UIPageViewController gesture recognizer(s) to enable swipe to delete
The gestures are attached to its scrollView, and this one is not a public attribute. Anyway I use this extension to get the scrollView :
extension UIPageViewController {
public var scrollView: UIScrollView? {
for view in self.view.subviews {
if let scrollView = view as? UIScrollView {
return scrollView
}
}
return nil
}
}
Then you want its panGesture :
pageController.scrollView?.panGestureRecognizer
UIPageViewController returns no Gesture Recognizers in iOS 6
There is a bug filed in radar for this behavior. So, I bet that until Apple fixes it there will be no chance to solve this.
One workaround that comes to my mind is laying a transparent subview on top of your UIPageViewController
and add to it a UIPanGestureRecognizer
to intercept that kind of gesture and not forward further. You could enable this view/recognizer when disabling the gesture is required.
I tried it with a combination of Pan and Tap gesture recognizers and it works.
This is my test code:
- (void)viewDidLoad {
[super viewDidLoad];
UIPanGestureRecognizer* g1 = [[[UIPanGestureRecognizer alloc] initWithTarget:self
action:@selector(g1Pan:)] autorelease];
[self.view addGestureRecognizer:g1];
UITapGestureRecognizer* s1 = [[[UITapGestureRecognizer alloc] initWithTarget:self
action:@selector(g1Tap:)] autorelease];
[self.view addGestureRecognizer:s1];
UIView* anotherView = [[[UIView alloc]initWithFrame:self.view.bounds] autorelease];
[self.view addSubview:anotherView];
UIPanGestureRecognizer* g2 = [[[UIPanGestureRecognizer alloc] initWithTarget:self
action:@selector(g2Pan:)] autorelease];
[anotherView addGestureRecognizer:g2];
}
When g2
is enabled, it will prevent g1
from being recognized. On the other hand, it will not prevent s1 from being recognized.
I understand this is hack, but in the face of a seeming bug in UIPageViewController
(at least, actual behavior is blatantly different from what the reference states), I cannot see any better solution.
How to tell whether a UIPageViewController page turn is due to a swipe/pan or a tap
The question is really how to determine which of several gesture recognizers is responsible for something happening.
The key thing to understand is that any gesture recognizer can have more than one target/action installed. The UIPageViewController sets its own target/actions internally, but that doesn't preclude others being added.
When setting up a sub-class of UIPageViewController after it has been loaded, or instantiated, add the following code (or Swift code equivalent):
for (UIGestureRecognizer *gr in self.gestureRecognizers) {
if ([gr class] == [UIPanGestureRecognizer class])
[gr addTarget:self action:@selector(panGestureOccurred)];
else if ([gr class] == [UITapGestureRecognizer class])
[gr addTarget:self action:@selector(tapGestureOccurred)];
}
Then, add the two target/action methods to the sub-class:
- (void)tapGestureOccurred
{
// Set a flag here to rely on later after the page turn
}
- (void)panGestureOccurred
{
// Reset a flag here to rely on later after the page turn
}
This obviously generalizes for any other types of gestures, but currently only the pan and tap gestures appear to be supported by a UIPageViewController
.
panGestureOccurred
will be called multiple times while the user is touching the screen and moving a finger/stylus. The tapGestureOccurred
is only called once.
But only one of the two methods will be called, depending on which gesture recognizer the page view controller in its infinite wisdom decides wins. This all seems to work much more robustly than the too-low level touchesBegan
and touchesMoved
idea posited in the original question.
UIPageViewController detecting pan gestures
I figured out how to do this. Basically a UIPageViewController uses UIScrollViews as its subviews. I created a loop and set all the subviews that are UIScrollViews and assigned their delegates to my ViewController.
/**
* Set the UIScrollViews that are part of the UIPageViewController to delegate to this class,
* that way we can know when the user is panning left/right
*/
-(void)initializeScrollViewDelegates
{
UIScrollView *pageScrollView;
for (UIView* view in self.pageViewController.view.subviews){
if([view isKindOfClass:[UIScrollView class]])
{
pageScrollView = (UIScrollView *)view;
pageScrollView.delegate = self;
}
}
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
NSLog(@"Im scrolling, yay!");
}
Customizing UIPageViewController gesture recognizers Scroll behavior
After trying so many hacks, I finally got solution.
What i did, first got the pageviewcontroller scorll view in a property
for (UIView *view in mypageviewcontroller.view.subviews) {
if([view isKindOfClass:[UIScrollView class]])
{
pagescrollview= (UIScrollView *)view;
}
}
Then assigned pan gesture to pageviewcontroller scorllview and gesture delegate.
UIPanGestureRecognizer* g1 = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(gesture)];
[g1 setDelegate:self];
[pagescrollview addGestureRecognizer:g1];
Then in gesture delegate , i checked whether the gesture starts from my desire points, if starts from a location that should scroll pageviewcontroller then this delegate method should return no to enable original pagescorllview gesture to receive pan gesture.
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]) {
CGPoint touchPoint = [touch locationInView:self.view];
if(floor(NSFoundationVersionNumber)<=NSFoundationVersionNumber_iOS_6_1)
touchPoint.y -=44;
else
touchPoint.y -=64;
PGNavigationController *nav =ViewControllerArray[VisiblePageindex];
PageContentVC *pagecontentvc = ((PageContentVC *) nav.visibleViewController);
if (touchPoint.y > pagecontentvc.leftpanalbtn.frame.origin.y && (pagecontentvc.leftpanalbtn.frame.size.height+pagecontentvc.leftpanalbtn.frame.origin.y )>touchPoint.y && touchPoint.x >pagecontentvc.leftpanalbtn.frame.origin.x
&& touchPoint.x<(pagecontentvc.leftpanalbtn.frame.origin.x+pagecontentvc.leftpanalbtn.frame.size.width)) {
return NO;
}
else if (touchPoint.y > pagecontentvc.rightpanalbtn.frame.origin.y && (pagecontentvc.rightpanalbtn.frame.size.height+pagecontentvc.rightpanalbtn.frame.origin.y )>touchPoint.y && touchPoint.x >pagecontentvc.rightpanalbtn.frame.origin.x
&& touchPoint.x<(pagecontentvc.rightpanalbtn.frame.origin.x+pagecontentvc.rightpanalbtn.frame.size.width))
{
return NO;
}
if( touchPoint.y>282 && touchPoint.x>118 &&touchPoint.y<282+75 && touchPoint.x < 118+85)
{
return NO;
}
// else if()
}
return YES;
}
hope it help someone else.
how to block swipe gesture of UIPageViewController in swift?
You can use the below extension enable or disable the SwipeGesture
extension UIPageViewController {
func enableSwipeGesture() {
for view in self.view.subviews {
if let subView = view as? UIScrollView {
subView.isScrollEnabled = true
}
}
}
func disableSwipeGesture() {
for view in self.view.subviews {
if let subView = view as? UIScrollView {
subView.isScrollEnabled = false
}
}
}
}
Related Topics
How to Play a Local Video With Swift
How to Detect Collision in Swift, Sprite Kit
How to Add Image and Text in Uitextview in Ios
In Ios13 the Status Bar Background Colour Is Different from the Navigation Bar in Large Text Mode
Replacement For Deprecated -Sizewithfont:Constrainedtosize:Linebreakmode: in iOS 7
Run App For More Than 10 Minutes in Background
How Would I Tint an Image Programmatically on Ios
Swift Native Functions to Have Numbers as Hex Strings
Navigation Controller Push View Controller
Watchkit Extension - No Matching Provisioning Profiles Found
Didregisterforremotenotificationswithdevicetoken Not Called in Ios8, But Didregister...Settings Is
Uinavigationbar Hide Back Button Text
How to Determine If an Annotation Is Inside of Mkpolygonview (Ios)
Ios7 Uitextview Contentsize.Height Alternative
How to Create a Swift Date Object
A Swift Example of Custom Views For Data Input (Custom In-App Keyboard)