Вопрос

I'm using an UIPageViewController in my application and I wanted to have a few UIButtons inside it, sort of like a menu. The problem I have is that when I put an UIButton (or any other interactive element) near the edges of the screen and tap it, instead of the UIButton action being applied, what happens is that the page changes (because the tap on the edge of the screen changes the page on the UIPageViewController). I'd like to know if there's a way to make it so that the UIButton has higher priority than the UIPageViewController so that when I tap the button, it applies the appropriate action instead of changing the page.

Это было полезно?

Решение

I came here with the same problem. Split’s link has the answer.

Make your root view controller the delegate of each of the UIPageViewController’s gesture recognizers, then prevent touches from being delivered if they occur inside any UIControl:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
    return ([touch.view isKindOfClass:[UIControl class]] == NO);
}

Другие советы

UIPageViewController has two UIGestureRecognizers. You can access them via gestureRecognizers property. Determine which one is UITapGestureRecognizer and then use this. Hope this helps.

For people that just want to copy/paste code, here is mine :

// I don't want the tap on borders to change the page
-(void) desactivatePageChangerGesture {
     for (UIGestureRecognizer* gestureRecognizer in self.pageViewController.gestureRecognizers) {
        if ([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]) {
            gestureRecognizer.enabled = NO;
        }
    }
}

Just call this function after the UIPageViewController creation.

I had this same problem, and was unsure how to handle the UIGestureRecognizer delegate methods. This short example assumes you are using the "Page Based Application" project type in Xcode 4. Here is what I did:

  • In RootViewController.h, I made sure to announce that RootViewController would handle the UIGestureRecognizerDelegate protocol:

    @interface RootViewController : UIViewController <UIPageViewControllerDelegate, UIGestureRecognizerDelegate>
    
  • In RootViewController.m, I assigned RootViewController as the delegate for the UITapGestureRecognizer. This is done at the end of the viewDidLoad method. I did this by iterating over each gestureRecognizer to see which one was the UITapGestureRecognizer.

    NSEnumerator *gestureLoop = [self.view.gestureRecognizers objectEnumerator];
    id gestureRecognizer;
    while (gestureRecognizer = [gestureLoop nextObject]) {
        if ([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]) {
            [(UITapGestureRecognizer *)gestureRecognizer setDelegate:self];
        }
    }
    
  • Finally, I added the gestureRecognizer:shouldReceiveTouch method to the bottom of RootViewController.m (This is copied directly from Split's link):

    - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
        if ([touch.view isKindOfClass:[UIControl class]]) {
            // we touched a button, slider, or other UIControl
            return NO; // ignore the touch
        }
        return YES; // handle the touch
    }
    

Comment out these line from your code

self.view.gestureRecognizers = self.pageViewController.gestureRecognizers;

or use UIGestureRecognizer as told by Split

Hope this will help you

OLD ANSWER: If your UIPageViewController has a transitionStyle of UIPageViewControllerTransitionStyleScroll and you are in iOS 6.0+, then you can't use the gestureRecognizer:shouldReceiveTouch: method, because there is no way to set the delegate to self on the gestureRecognizers since pageViewController.gestureRecognizers will return nil. See UIPageViewController returns no Gesture Recognizers in iOS 6 for more information about that.

If you simply want to make sure your UIPageViewController passes along button touch events to a UIButton, you can use

for (UIScrollView *view in _pageViewController.view.subviews) {
    if ([view isKindOfClass:[UIScrollView class]]) {
        view.delaysContentTouches = NO;
    }
}

if you have a transitionStyle of UIPageViewControllerTransitionStyleScroll and you are in iOS 6.0+.

See this answer about why delaysContentTouches = NO is needed for some cases of a UIButton in a UIScrollView

UPDATE: After doing a little more research it appears that if your issue is that the UIButton click seems to only be called sometimes, then that is actually probably the desired behavior inside a UIScrollView. A UIScrollView uses the delaysContentTouches property to automatically determine if the user was trying to scroll or trying to press a button inside the scroll view. I would assume it is best to not alter this behavior to default to NO since doing so will result in an inability to scroll if the user's finger is over a button.

None of the solutions here where you intercept the UIPageViewController's tap gesture recognizers worked for me. I'm targeting iOS 8 and 9.

What worked is to override the functions touchesBegan, touchesCancelled, touchesMoved, and touchesEnded in my custom button which is a subclass of UIControl. Then I just manually send the .TouchUpInside control event if the touch began and ended within the frame of my custom button.

I didn't have to do anything special for the containing page view controller, or the view controller that contains the page view controller.

Swift 5 answer here should do the job.

pageViewController.view.subviews.compactMap({ $0 as? UIScrollView }).first?.delaysContentTouches = false

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top