禁用UIPageViewController反弹

为这个search了很多,但是还找不到合适的解决scheme。

是否有可能禁用UIPageViewController的反弹效果,仍然使用UIPageViewControllerTransitionStyleScroll

禁用UIPageViewController的反弹

  1. <UIScrollViewDelegate>委托添加到您的UIPageViewController的标题

  2. 将UIPageViewController的底层UIScrollView的委托设置为其父级viewDidLoad

     for (UIView *view in self.view.subviews) { if ([view isKindOfClass:[UIScrollView class]]) { ((UIScrollView *)view).delegate = self; break; } } 
  3. scrollViewDidScroll的实现是在用户超出边界时将contentOffset重置为原点( NOT(0,0),但是(bound.size.width,0) ),如下所示:

     - (void)scrollViewDidScroll:(UIScrollView *)scrollView { if (_currentPage == 0 && scrollView.contentOffset.x < scrollView.bounds.size.width) { scrollView.contentOffset = CGPointMake(scrollView.bounds.size.width, 0); } else if (_currentPage == totalViewControllersInPageController-1 && scrollView.contentOffset.x > scrollView.bounds.size.width) { scrollView.contentOffset = CGPointMake(scrollView.bounds.size.width, 0); } } 
  4. 最后, scrollViewWillEndDragging的实现是处理一个错误情况,当用户在第一页从左向右快速滑动时,第一页不会在左边反弹(由于上面的函数),但是会在由(或许)速度的滑动造成的。 最后,当弹回时,UIPageViewController会触发一个翻页到第二页(这当然不是预期的)。

     - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset { if (_currentPage == 0 && scrollView.contentOffset.x <= scrollView.bounds.size.width) { *targetContentOffset = CGPointMake(scrollView.bounds.size.width, 0); } else if (_currentPage == totalViewControllersInPageController-1 && scrollView.contentOffset.x >= scrollView.bounds.size.width) { *targetContentOffset = CGPointMake(scrollView.bounds.size.width, 0); } } 

禁用UIPageViewController的反弹

Swift 2.2

除了答案

1)将UIScrollViewDelegate添加到UIPageViewController

 extension PageViewController: UIScrollViewDelegate 

2)添加到viewDidLoad

 for view in self.view.subviews { if let scrollView = view as? UIScrollView { scrollView.delegate = self } } 

3)添加UIScrollViewDelegate方法

 func scrollViewDidScroll(scrollView: UIScrollView) { if currentIndex == 0 && scrollView.contentOffset.x < scrollView.bounds.size.width { scrollView.contentOffset = CGPoint(x: scrollView.bounds.size.width, y: 0) } else if currentIndex == totalViewControllers - 1 && scrollView.contentOffset.x > scrollView.bounds.size.width { scrollView.contentOffset = CGPoint(x: scrollView.bounds.size.width, y: 0) } } func scrollViewWillEndDragging(scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) { if currentIndex == 0 && scrollView.contentOffset.x < scrollView.bounds.size.width { scrollView.contentOffset = CGPoint(x: scrollView.bounds.size.width, y: 0) } else if currentIndex == totalViewControllers - 1 && scrollView.contentOffset.x > scrollView.bounds.size.width { scrollView.contentOffset = CGPoint(x: scrollView.bounds.size.width, y: 0) } } 

我不知道如何正确pipe理currentIndex但最终做了

 extension Main: UIPageViewControllerDelegate { func pageViewController(pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) { if completed { guard let viewController = pageViewController.viewControllers?.first, index = viewControllerDatasource.indexOf(viewController) else { fatalError("Can't prevent bounce if there's not an index") } currentIndex = index } } } 

UIPageViewController实际上并没有为你做很多事情。 您可以很容易地使用视图控制器的UIScrollView,并禁用该反弹。

只要做一些像

 int x=0; for (NSString *storyboardID in storyboardIDs){ UIViewController *vc = [storyboard instantiateViewControllerWithIdentifier:storyboardID]; [self addChildViewController:vc]; vc.view.frame = CGRectMake(x++*vc.view.frame.size.width, 0, vc.view.frame.size.width, vc.view.frame.size.height); [self.scrollView addSubview:vc.view]; [vc didMoveToParentViewController:self]; self.scrollView.contentSize = CGSizeMake(storyboardIDs.count*vc.view.frame.size.width, vc.view.frame.size.height); } 

如果您尝试在UIPageViewController.scrollView上禁用反弹, UIPageViewController.scrollView得到一个损坏的pageViewController 。 滑动将无法使用。 所以,不要试图做这样的事情:

 self.theScrollView.alwaysBounceHorizontal = NO; self.theScrollView.bounces = NO; 

UIPageViewController子视图中使用searchscrollView引用的解决scheme仅用于完全禁用滚动:

 @interface MyPageViewController : UIPageViewController @property (nonatomic, assign) BOOL scrollEnabled; @end @interface MyPageViewController () @property (nonatomic, weak) UIScrollView *theScrollView; @end @implementation MyPageViewController - (void)viewDidLoad { [super viewDidLoad]; for (UIView *view in self.view.subviews) { if ([view isKindOfClass:UIScrollView.class]) { self.theScrollView = (UIScrollView *)view; break; } } } - (void)setScrollEnabled:(BOOL)scrollEnabled { _scrollEnabled = scrollEnabled; self.theScrollView.scrollEnabled = scrollEnabled; } @end 

在UIPageViewController上禁用反弹的解决scheme:

  1. 创buildUIScrollView类别(例如CustomScrolling)。 UIScrollView已经是他们的手势识别器的代表了。
  2. 请注意,您的目标UIViewController (又名baseVC with UIPageViewController里面)通过AppDelegate共享。 否则,您可以使用运行时( #import <objc/runtime.h> )并将引用属性(添加到控制器baseVC )添加到类别中。
  3. 实施类别:

     @interface UIScrollView (CustomScrolling) <UIGestureRecognizerDelegate> @end @implementation UIScrollView (CustomScrolling) - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer { UIViewController * baseVC = [(AppDelegate *)[[UIApplication sharedApplication] delegate] baseVC]; if (gestureRecognizer.view == baseVC.pageViewController.theScrollView) { NSInteger page = [baseVC selectedIndex]; NSInteger total = [baseVC viewControllers].count; UIPanGestureRecognizer *recognizer = (UIPanGestureRecognizer *)gestureRecognizer; CGPoint velocity = [recognizer velocityInView:self]; BOOL horizontalSwipe = fabs(velocity.x) > fabs(velocity.y); if (!horizontalSwipe) { return YES; } BOOL scrollingFromLeftToRight = velocity.x > 0; if ((scrollingFromLeftToRight && page > 0) || (!scrollingFromLeftToRight && page < (total - 1))) { return YES; } return NO; } return YES; } @end 
  4. 在您的baseVC中使用UIPageViewController导入类别文件#import "UIScrollView+CustomScrolling.h"

编辑:不要使用这个解决scheme。 后来我了解到,这引入了一个大约5%的时间,用户不能在同一方向页面的错误。 他们必须回头,然后再继续前进。

如果您使用的是UIPageViewControllerDataSource ,则相对简单的解决方法(以及一点点哈希)是每次pageViewController:viewControllerBeforeViewController: delegate方法时禁用弹跳。 这是一个示例实现:

 @interface YourDataSourceObject () @property (strong, nonatomic) UIScrollView *scrollView; @end @implementation - (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController { if (!self.scrollView) { for (UIView *view in pageViewController.view.subviews) { if ([view isKindOfClass:[UIScrollView class]]) { self.scrollView = (UIScrollView *)view; } } } self.scrollView.bounces = NO; // Your other logic to return the correct view controller. } @end 

这是我的单行快速修复(Swift 3):

_ = self.view.subviews.map { ($0 as? UIScrollView)?.bounces = false}

你可以把它粘在viewDidLoad中(如果你在控制器的某个地方也有你自己的scrollView,你应该相应的修改)。 一个UIPageViewController只是一个在其主视图中具有UIScrollView UIViewController (几乎没有),以及一些预设的scrollView属性/委托简化。

请注意,如果你做了_ = self.view.subviews.map { print(($0 as? UIScrollView)?.isPagingEnabled)}你会在控制台中看到true