UISlider inside UIPageViewController

2020-08-12 09:45发布

问题:

I have a PageViewController which is initialized like this:

self.pageViewController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll
                          navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil];

On one of the pages, there's a UISlider.

My problem is that when I have transitionstyle set to UIPageViewControllerTransitionStyleScroll, it takes 150-200 ms before beginTrackingWithTouch is invoked on the slider. This behavior is not seen when I use UIPageViewControllerTransitionStylePageCurl, where the UISlider is selected instantly.

This means that unless the user waits a bit before dragging the slider (a video progress), the page will turn instead, which is far from ideal.

The Page curl animation does not meet the demands of the app, so any explanation or workaround is appreciated.

回答1:

Since with UIPageViewControllerTransitionStyleScroll gesture recognizers isn't available, you can use this:

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


回答2:

I solved this issue by add a pan gesture on UISlider and set:

self.sliderGesture.cancelsTouchesInView = NO;  // make touch always triggered

and implement delegate method like:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
    return otherGestureRecognizer.view.superview == self.parentViewController.view;
}

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
    // only receive touch in slider
    CGPoint touchLocation = [touch locationInView:self.view];
    return CGRectContainsPoint(self.slider.frame, touchLocation);
}


回答3:

You can try to set the delegate of the page view controller gestures to the root view controller:

for (UIGestureRecognizer* gestureRecognizer in self.pageViewController.gestureRecognizers) {
    gestureRecognizer.delegate = self;
}

And then prevent the touch of the gestures if it appears inside UISlider which is a subclass of UIControl:

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


回答4:

What helped me was to add pan-gesture-recognizer to UIView which holds UISlider, so in the end I have UIPageViewController->UIScrollView->...->MyView->UISlider The 'MyView' thing had pan gesture registered to it which did nothing, but served just to NOT propagate events to scroll view.