如何禁用UIPageViewController的滑动手势?

149
在我的情况下,父级UIViewController 包含UIPageViewController ,后者包含UINavigationController ,后者又包含UIViewController。 我需要在最后一个视图控制器中添加一个滑动手势,但是滑动手势被处理得好像它们属于页面视图控制器一样。 我尝试通过编程和xib来实现此操作,但没有任何结果。
因此,我理解的是,在UIPageViewController处理其手势之前,我无法实现我的目标。如何解决这个问题?

9
使用 pageViewControllerObject.view.userInteractionEnabled = NO;,将页面视图控制器对象的用户交互功能设置为禁用。 - Srikanth
9
不正确,因为它也会阻挡所有内容。但我只需要阻止它的手势。 - user2159978
你为什么要这样做?你希望应用程序有什么行为表现?听起来你正在过度复杂化架构。你能解释一下你想要的行为表现吗? - Fogmeister
1
UIPageViewController 中的一个页面包含 UINavigationController。客户希望在滑动手势时弹出导航控制器(如果可能的话),但是页面视图控制器处理滑动手势。 - user2159978
1
@Srikanth 谢谢,这正是我需要的!当我以编程方式触发页面更改并有人用手指滑动打断时,我的页面视图会崩溃,所以我不得不在代码中每次触发页面更改时暂时禁用滑动... - Kuba Suder
显示剩余2条评论
25个回答

294
记录在文档中的防止 UIPageViewController 滚动的方法是不将 dataSource 属性赋值。如果你分配了数据源,它会进入“基于手势”的导航模式,这正是你想要避免的。
没有数据源时,可以使用 setViewControllers:direction:animated:completion 方法手动提供视图控制器,并且它将根据需要在视图控制器之间移动。
以上内容可以从 Apple 对 UIPageViewController 的说明(概述,第二段)推断出:
  

为了支持基于手势的导航,必须使用数据源对象提供视图控制器。


1
我也更喜欢这个解决方案。你可以将旧的数据源保存在一个变量中,将数据源设置为nil,然后恢复旧的数据源以重新启用滚动。虽然不是很干净,但比遍历底层视图层次结构以查找滚动视图要好得多。 - Matt R
3
禁用数据源会在响应文本字段子视图的键盘通知时引起问题。遍历视图的子视图以查找UIScrollView似乎更可靠。 - A. R. Younce
10
这是否会移除屏幕底部的圆点呢?如果没有滑动和圆点,那么使用PageViewController也就没什么意义了。我猜提问者想要保留这些圆点。 - Leo Flaherty
1
A. R Younce是正确的。如果您在响应键盘通知时禁用PageViewController的数据源(即:当键盘弹出时,您不希望用户水平滚动),那么当您将PageViewController的数据源设置为nil时,键盘将立即消失。 - micnguyen
我无法感谢你们的帮助了... 我一直在困惑中,试图解决我的代码中不同手势之间相互冲突的错误。这次帮助让我节省了很多时间。 - Abolfoooud
显示剩余4条评论

85
for (UIScrollView *view in self.pageViewController.view.subviews) {

    if ([view isKindOfClass:[UIScrollView class]]) {

        view.scrollEnabled = NO;
    }
}

6
这个可以保持页面控制,很不错。 - Marcus Adams
1
这是一个很好的方法,因为它保持了页面控制(小点)。当手动更改页面时,您将需要更新它们。我使用了这个答案 https://dev59.com/C27Xa4cB1Zd3GeqPq4kw#28356383 - Simon Bøgh
1
为什么不使用 for (UIView *view in self.pageViewController.view.subviews) { - dengApro
请注意,用户仍然可以通过在左右半页控件上轻触来在页面之间导航。 - MasterCarl

72

我将user2159978的答案翻译成了Swift 5.1

func removeSwipeGesture(){
    for view in self.pageViewController!.view.subviews {
        if let subView = view as? UIScrollView {
            subView.isScrollEnabled = false
        }
    }
}

37

将@lee(@user2159978)的解决方案作为扩展实现:

extension UIPageViewController {
    var isPagingEnabled: Bool {
        get {
            var isEnabled: Bool = true
            for view in view.subviews {
                if let subView = view as? UIScrollView {
                    isEnabled = subView.isScrollEnabled
                }
            }
            return isEnabled
        }
        set {
            for view in view.subviews {
                if let subView = view as? UIScrollView {
                    subView.isScrollEnabled = newValue
                }
            }
        }
    }
}

使用方法:(在UIPageViewController中)

self.isPagingEnabled = false

为了更加优雅,您可以添加以下内容: for view in view.subviews { if let subView = view as? UIScrollView { return subView } } return nil }``` - Wagner Sales
这里的getter仅返回最后一个子视图的启用状态。 - Andrew Duncan
@AndrewDuncan,这有什么问题吗?除非你有多个滚动视图,否则它可以解决问题...? - OhadM
pagingEnabled 不是一个很酷的名称。isSwipingEnabled 更好。 - hariszaman
可能不太酷,但遵循命名规范 :) - LinusGeffarth

8

注意:此答案仅适用于页面翻页样式。 Jessedc的回答 更好:不管样式如何,均有效,并依赖于文档行为

UIPageViewController公开了其手势识别器数组,您可以使用它们来禁用它们:

// myPageViewController is your UIPageViewController instance
for (UIGestureRecognizer *recognizer in myPageViewController.gestureRecognizers) {
    recognizer.enabled = NO;
}

8
你测试过你的代码了吗?myPageViewController.gestureRecognizers 总是为空。 - user2159978
11
你的解决方案似乎适用于翻页效果,但在我的应用程序中,我使用滚动视图样式。 - user2159978
8
无法水平滚动。返回值始终为空数组。 - Khanh Nguyen
你们解决了滚动样式的问题吗? - Esqarrouth

8

我已经在这个问题上奋斗了一段时间,想着应该发布我的解决方案,接着Jessedc的答案;移除PageViewController的数据源。

我将以下代码添加到我的PgeViewController类中(与storyboard中的页面视图控制器相关联,继承了UIPageViewControllerUIPageViewControllerDataSource):

static func enable(enable: Bool){
    let appDelegate  = UIApplication.sharedApplication().delegate as! AppDelegate
    let pageViewController = appDelegate.window!.rootViewController as! PgeViewController
    if (enable){
        pageViewController.dataSource = pageViewController
    }else{
        pageViewController.dataSource = nil
    }
}

这样,每当每个子视图出现时(在这种情况下是禁用它),就可以调用它。
override func viewDidAppear(animated: Bool) {
    PgeViewController.enable(false)
}

我希望这能帮助有需要的人们,虽然不是很完美,但也不会感觉太过突兀等。
编辑:如果有人想将其翻译成Objective-C,请随意 :)

6
一个有用的UIPageViewController扩展,可启用和禁用滑动。
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
            }
        }
    }
}

5
如果您想让您的UIPageViewController保持其滑动功能,同时允许您的内容控件使用它们的功能(如滑动以删除等),只需在UIPageViewController中关闭canCancelContentTouches
将此放置在您的UIPageViewControllerviewDidLoad函数中。(Swift)
if let myView = view?.subviews.first as? UIScrollView {
    myView.canCancelContentTouches = false
}
UIPageViewController有一个自动生成的子视图来处理手势。我们可以防止这些子视图取消内容手势。
从... 在PageViewController中的tableView上进行滑动以删除

谢谢Carter。我已经尝试了你的解决方案。然而,有时pageViewController仍会从子表视图中夺取水平滑动操作。我该如何确保当用户在子表视图上滑动时,始终优先考虑表视图获取手势? - David Liu
这个问题的最佳答案! - Yev Kanivets

5

Swifty 方式回答 @lee 的问题

extension UIPageViewController {
    var isPagingEnabled: Bool {
        get {
            return scrollView?.isScrollEnabled ?? false
        }
        set {
            scrollView?.isScrollEnabled = newValue
        }
    }

    var scrollView: UIScrollView? {
        return view.subviews.first(where: { $0 is UIScrollView }) as? UIScrollView
    }
}

4

以下是我使用 Swift 的解决方案

extension UIPageViewController {
    var isScrollEnabled: Bool {
        set {
            (self.view.subviews.first(where: { $0 is UIScrollView }) as? UIScrollView)?.isScrollEnabled = newValue
        }
        get {
            return (self.view.subviews.first(where: { $0 is UIScrollView }) as? UIScrollView)?.isScrollEnabled ?? true
        }
    }
}

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