禁用UIPageViewController的弹性效果

58

我搜索了很多,但还没有找到合适的解决方案。

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


1
如果你想要去掉那个弹跳效果,你可以使用UIPageViewControllerTransitionStylePageCurl样式。 - Pawan Rai
1
@pawan 我想使用UIPageViewControllerTransitionStyleScroll。 - Mario
17个回答

0

应该实现UIScrollViewDelegate方法来限制边缘滚动

class MyPageViewController: UIPageViewController {

    override func loadView() {
        super.loadView()
    
        view.subviews.forEach { sv in
            if let scrollView = sv as? UIScrollView {
                scrollView.delegate = self
            }
        }
    }
}

extension MyPageViewController: UIScrollViewDelegate {

    func scrollViewDidScroll(_ scrollView: UIScrollView) {
            
        if currentPage == 0, scrollView.contentOffset.x + scrollView.contentInset.left < 0 {
            scrollView.contentOffset = CGPoint(x: -scrollView.contentInset.left, y: scrollView.contentOffset.y)
        }
        else if currentPage == pageCount - 1, scrollView.contentInset.right < 0, scrollView.contentOffset.x + scrollView.contentInset.right > 0 {
            scrollView.contentOffset = CGPoint(x: -scrollView.contentInset.right, y: scrollView.contentOffset.y)
        }
    }

    func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {

        if currentPage == 0, scrollView.contentOffset.x + scrollView.contentInset.left < 0 {
            targetContentOffset.pointee = CGPoint(x: -scrollView.contentInset.left, y: targetContentOffset.pointee.y)
        }
        else if currentPage == pageCount - 1, scrollView.contentInset.right < 0, scrollView.contentOffset.x + scrollView.contentInset.right > 0 {
            targetContentOffset.pointee = CGPoint(x: -scrollView.contentInset.right, y: targetContentOffset.pointee.y)
        }
    }
}

这里的currentPage是当前页面索引,pageCount是总页数。


0

要在UIPageViewController上禁用反弹,您可以这样做(在Swift 5中),假设您将myPages var声明为UIViewController数组:

// 添加滚动委托

class myController: UIPageViewController, UIScrollViewDelegate { ... }

// 声明 currentIdx 和 scrollview 变量

private var currentIdx: Int = 0
private var scrollview: UIScrollView?

// 在 ViewDidLoad 中,添加委托和股票滚动视图

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

// 在你的代码中可以像这样检索当前索引

guard let currentViewController = viewControllers?.first, let currentIndex = myPages.firstIndex(of: currentViewController) else {
   return
}

currentIdx = index

// 声明私有函数来计算需要的反弹次数

private func setBounces() {
    let max = myPages.count - 1
    scrollview?.bounces = currentIdx > 0 && currentIdx < max
}

// 添加UIScrollViewDelegate方法

func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
    setBounces()
}

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    setBounces()
}

-1

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让事情变得更加复杂,我能看到的基本用法的唯一优点是比UIScrollView更容易实现页面翻页效果。更不用说使用页面控制器只需要大约10行代码,而使用UIScrollView则需要100行代码 :) - Dimitar Marinov

-1
如果你尝试禁用UIPageViewController.scrollView的弹跳效果,你肯定会导致pageViewController出现问题:滑动操作将无法正常工作。所以,请不要这样做。
self.theScrollView.alwaysBounceHorizontal = NO;
self.theScrollView.bounces = NO;

使用在UIPageViewController子视图中搜索scrollView引用的解决方案,以完全禁用滚动:

@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中的弹跳解决方案:

  1. 创建UIScrollView类别(例如CustomScrolling)。 UIScrollView已经是其手势识别器的委托。
  2. 请注意,您的目标UIViewController(即内部带有UIPageViewControllerbaseVC)通过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. 在使用UIPageViewController的baseVC中导入类别文件#import "UIScrollView+CustomScrolling.h"


你能与整个班级分享一下要点吗? - el.severo
@el.severo 实际上我已经做过了。唯一的弱点是在你的UIScrollView (CustomScrolling)类别中获取指向baseVC的指针。 - sig

-2

编辑:不要使用此解决方案。仅供参考和教育目的后来我发现这会引入一个 bug,大约有 5% 的情况下,用户不能以同一方向翻页。他们必须返回到上一页,然后再次前进才能继续。

如果您正在使用 UIPageViewControllerDataSource,一个相对简单的解决方法(有点 hacky)是在每次调用 pageViewController:viewControllerBeforeViewController: 代理方法时禁用弹跳。以下是一个示例实现:

@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

4
你发了一条“不要使用这个解决方案”的帖子。为什么不直接删除这个答案呢?毕竟你也不会因此失去任何积分。 - Michael Dautermann
3
@MichaelDautermann 我会把它留在这里。为了教育目的,看到不好的模式也是有益的。由于SO像一个巨大的分散手册,所以这很有价值 ;) - sznowicki
bounces = NO 完全不起作用。它只是停止滚动。 - Zaporozhchenko Oleksandr

-2

可行的解决方案 - Swift 5

UIPageViewController的子视图中找到UIScrollView实例,

然后将bounces属性设置为false,就可以轻松解决问题。

let vc = UIPageViewController(transitionStyle: UIPageViewController.TransitionStyle.scroll, navigationOrientation: UIPageViewController.NavigationOrientation.horizontal, options: [:])
for subview in vc.view.subviews {
  if let scrollView = subview as? UIScrollView {
    scrollView.bounces = false
    break
  }
}
        

-2

这对我来说在viewDidLoad中起了作用:

for subview in pager.view.subviews {
    if let scrollView = subview as? UIScrollView {
        scrollView.bounces = false
        scrollView.alwaysBounceHorizontal = false
        break
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接