如何禁用 UIPageViewController 的滑动手势?

在我的例子中,父 UIViewController包含 UIPageViewController,它包含 UINavigationController,它包含 UIViewController。我需要添加一个滑动姿态到最后一个视图控制器,但滑动处理好像他们属于页面视图控制器。我尝试用编程方式和通过 xib 这样做,但没有结果。

因此,据我所知,我不能实现我的目标,直到 UIPageViewController处理它的手势。如何解决这个问题?

101889 次浏览

编辑 : 此答案仅适用于页面卷曲样式。杰西德克的回答要好得多: 不管风格如何,都依赖于 记录在案的行为

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

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


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


view.scrollEnabled = NO;
}
}

防止 UIPageViewController滚动的文档化方法是不分配 dataSource属性。如果你分配了数据源,它就会进入“基于手势”的导航模式,而这正是你试图阻止的。

在没有数据源的情况下,如果需要,可以使用 setViewControllers:direction:animated:completion方法手动提供视图控制器,它将根据需要在视图控制器之间移动。

以上内容可以从 苹果的 UIPageViewController 文档(概述,第二段)中推导出来:

若要支持基于姿态的导航,必须使用数据源对象提供视图控制器。

我把 User2159978的答案翻译成 Swift 5.1

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

我已经为此抗争了一段时间,并认为我应该发布我的解决方案,在 Jessec 的回答之后,删除 PageViewController 的数据源。

我将其添加到 PgeViewController 类(链接到故事板中的页面视图控制器,继承 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,请这样做:)

如果你想让你的 UIPageViewController保持它的滑动能力,同时允许你的内容控件使用他们的功能(滑动删除等) ,只需关闭 UIPageViewController中的 canCancelContentTouches

把这个放进你的 UIPageViewControllerviewDidLoad功能里

if let myView = view?.subviews.first as? UIScrollView {
myView.canCancelContentTouches = false
}

UIPageViewController有一个自动生成的子视图来处理手势。我们可以防止这些子视图取消内容手势。

从..。

在 pageViewController 中的 tableView 上滑动以删除

作为扩展实现@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

与@user3568340类似

Swift 4

private var _enabled = true
public var enabled:Bool {
set {
if _enabled != newValue {
_enabled = newValue
if _enabled {
dataSource = self
}
else{
dataSource = nil
}
}
}
get {
return _enabled
}
}

翻译@user2159978对 C # 的回复:

foreach (var view in pageViewController.View.Subviews){
var subView = view as UIScrollView;
if (subView != null){
subView.ScrollEnabled = enabled;
}
}

感谢@user2159978的回复。

我让它变得更容易理解。

- (void)disableScroll{
for (UIView *view in self.pageViewController.view.subviews) {
if ([view isKindOfClass:[UIScrollView class]]) {
UIScrollView * aView = (UIScrollView *)view;
aView.scrollEnabled = NO;
}
}
}

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
}
}
}
}

我是这样解决的(Swift 4.1)

if let scrollView = self.view.subviews.filter({$0.isKind(of: UIScrollView.self)}).first as? UIScrollView {
scrollView.isScrollEnabled = false
}

(Swift 4)可以删除 pageViewController 的手势识别器:

pageViewController.view.gestureRecognizers?.forEach({ (gesture) in
pageViewController.view.removeGestureRecognizer(gesture)
})

如果你喜欢扩展:

extension UIViewController{
func removeGestureRecognizers(){
view.gestureRecognizers?.forEach({ (gesture) in
view.removeGestureRecognizer(gesture)
})
}
}

pageViewController.removeGestureRecognizers

这样声明:

private var scrollView: UIScrollView? {
return pageViewController.view.subviews.compactMap { $0 as? UIScrollView }.first
}

然后像这样使用它:

scrollView?.isScrollEnabled = true //false

@ 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
}
}

我找到的答案对我来说看起来非常令人困惑或不完整,所以这里有一个完整的可配置的解决方案:

第一步:

让每个 PVC 元素负责判断是否启用了左右滚动。

protocol PageViewControllerElement: class {
var isLeftScrollEnabled: Bool { get }
var isRightScrollEnabled: Bool { get }
}
extension PageViewControllerElement {
// scroll is enabled in both directions by default
var isLeftScrollEnabled: Bool {
get {
return true
}
}


var isRightScrollEnabled: Bool {
get {
return true
}
}
}

每个 PVC 视图控制器都应该实现上述协议。

第二步:

在 PVC 控制器中,如果需要,禁用滚动:

extension SomeViewController: PageViewControllerElement {
var isRightScrollEnabled: Bool {
get {
return false
}
}
}


class SomeViewController: UIViewController {
// ...
}

第三步:

将有效的滚动锁定方法添加到 PVC 中:

class PVC: UIPageViewController, UIPageViewDelegate {
private var isLeftScrollEnabled = true
private var isRightScrollEnabled = true
// ...


override func viewDidLoad() {
super.viewDidLoad()
// ...
self.delegate = self
self.scrollView?.delegate = self
}
}


extension PVC: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
print("contentOffset = \(scrollView.contentOffset.x)")


if !self.isLeftScrollEnabled {
disableLeftScroll(scrollView)
}
if !self.isRightScrollEnabled {
disableRightScroll(scrollView)
}
}


private func disableLeftScroll(_ scrollView: UIScrollView) {
let screenWidth = UIScreen.main.bounds.width
if scrollView.contentOffset.x < screenWidth {
scrollView.setContentOffset(CGPoint(x: screenWidth, y: 0), animated: false)
}
}


private func disableRightScroll(_ scrollView: UIScrollView) {
let screenWidth = UIScreen.main.bounds.width
if scrollView.contentOffset.x > screenWidth {
scrollView.setContentOffset(CGPoint(x: screenWidth, y: 0), animated: false)
}
}
}


extension UIPageViewController {
var scrollView: UIScrollView? {
return view.subviews.filter { $0 is UIScrollView }.first as? UIScrollView
}
}

第四步:

在到达新屏幕时更新与滚动相关的属性(如果您手动转换到某个屏幕,不要忘记调用 enerleScroll 方法) :

func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
let pageContentViewController = pageViewController.viewControllers![0]
// ...


self.enableScroll(for: pageContentViewController)
}


private func enableScroll(for viewController: UIViewController) {
guard let viewController = viewController as? PageViewControllerElement else {
self.isLeftScrollEnabled = true
self.isRightScrollEnabled = true
return
}


self.isLeftScrollEnabled = viewController.isLeftScrollEnabled
self.isRightScrollEnabled = viewController.isRightScrollEnabled


if !self.isLeftScrollEnabled {
print("Left Scroll Disabled")
}
if !self.isRightScrollEnabled {
print("Right Scroll Disabled")
}
}

枚举子视图以查找 UIPageViewController的 scrollView 对我来说不起作用,因为我在页面控制器子类中找不到任何 scrollView。所以我想做的是禁用手势识别器,但要小心不要禁用必要的手势识别器。

所以我想到了这个:

if let panGesture = self.gestureRecognizers.filter({$0.isKind(of: UIPanGestureRecognizer.self)}).first
panGesture.isEnabled = false
}

把它放到 viewDidLoad()里面,你就可以了!

 override func viewDidLayoutSubviews() {
for View in self.view.subviews{
if View.isKind(of: UIScrollView.self){
let ScrollV = View as! UIScrollView
ScrollV.isScrollEnabled = false
}


}
}

把这个添加到你的 pageviewcontroller 类中。100% 工作

只需在 UIPageViewController子类中添加这个控件属性:

var isScrollEnabled = true {
didSet {
for case let scrollView as UIScrollView in view.subviews {
scrollView.isScrollEnabled = isScrollEnabled
}
}
}

这里有一种比大多数答案建议的更简单的方法,即在 viewControllerBeforeviewControllerAfter dataSource 回调中返回 nil

这将禁用 iOS11 + 设备上的滚动手势,同时保留使用 dataSource的可能性(例如用于页面指示器的 presentationIndex/presentationCount)

它还禁用通过。IOS11-13的 pageControl(底部的点)。在 iOS14上,底部点的导航可以使用 UI卖相代理来禁用。

extension MyPageViewController: UIPageViewControllerDataSource {
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
return nil
}
    

func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
return nil
}
}

pageViewController.view.isUserInteractionEnabled = false

这将禁用与页面的所有交互。如果你需要用户能够与内容交互-这不是你的解决方案。

我的解决办法是迅速

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
}
}
}

更有效的返回方法是在 viewdiload 上调用这个方法(Swift 5) :

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