UIPageViewControllerのスワイプジェスチャーを無効にするにはどうすればよいですか?


125

私の場合、親をUIViewController含む、UIPageViewControllerを含む、UINavigationControllerを含むUIViewController。最後のビューコントローラーにスワイプジェスチャーを追加する必要がありますが、スワイプはページビューコントローラーに属しているかのように処理されます。これをプログラムとxibの両方で実行しようとしましたが、結果はありませんでした。

だから私は理解しているようにUIPageViewController、ジェスチャーを処理するまで目標を達成することはできません。この問題を解決するには?


7
使用pageViewControllerObject.view.userInteractionEnabled = NO;
Srikanth 2014

6
すべてのコンテンツもブロックするため、正しくありません。しかし、私はそのジェスチャーのみをブロックする必要があります
user2159978

なぜあなたはこれをやっている?アプリにどのような動作をさせたいですか?アーキテクチャが複雑すぎるようです。どのような行動を目指しているのか説明してください。
フォグマイスター2014

内のページの一つUIPageViewControllerがありますUINavigationController。お客様は、スワイプジェスチャーでナビゲーションコントローラーをポップしたい(可能な場合)が、代わりにページビューコントローラーがスワイプを処理する
user2159978

@Srikanthありがとう、それがまさに私が必要としていたことです!私はプログラム的にページの変更をトリガーしたときに私のページビューがクラッシュしていましたし、私は一時的に無効にスワイプするページの変更がコードでトリガされるたびに持っていたので、誰かが...、スワイプでそれを中断
クバSuder

回答:


262

UIPageViewControllerスクロールを防ぐための文書化された方法は、dataSourceプロパティを割り当てないことです。データソースを割り当てると、データソースは「ジェスチャーベース」のナビゲーションモードに移行します。これは、回避しようとしているものです。

データソースがない場合は、必要なときに手動でビューコントローラーを提供します setViewControllers:direction:animated:completionメソッドし、オンデマンドでビューコントローラー間を移動します。

上記は、UIPageViewController(概要、2番目の段落)のAppleのドキュメントから推測できます。

ジェスチャーベースのナビゲーションをサポートするには、データソースオブジェクトを使用してビューコントローラーを提供する必要があります。


1
私もこの解決策を好みます。古いデータソースを変数に保存し、データソースをnilに設定してから、古いデータソースを復元してスクロールを再度有効にできます。それほどきれいではありませんが、基礎となるビュー階層を走査してスクロールビューを見つけるよりもはるかに優れています。
Matt R

3
データソースを無効にすると、テキストフィールドサブビューのキーボード通知に応答して無効にするときに問題が発生します。ビューのサブビューを繰り返し処理してUIScrollViewを見つける方が、信頼性が高いようです。
AR Younce、2014

9
これで画面下のドットが消えませんか?また、スワイプもドットもなければ、PageViewControllerを使用しても意味がありません。質問者はドットを保持したいと思っています。
Leo Flaherty

これは、ページが非同期で読み込まれ、ユーザーがスワイプジェスチャーをするときに直面していた問題を解決するのに役立ちました。ブラボー:)
ジャック

1
A. Rヨンセは正しいです。キーボード通知に応答してPageViewControllerのデータソースを無効にする場合(つまり、キーボードが上にあるときにユーザーが水平方向にスクロールしないようにする場合)、PageViewControllerのデータソースを表示しないと、キーボードはすぐに非表示になります。
micnguyen 2015年

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

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

        view.scrollEnabled = NO;
    }
}

5
これはページコントロールを保持します。
Marcus Adams、

1
ページコントロール(小さなドット)を保持するため、これは優れたアプローチです。ページを手動で変更する場合は、それらを更新する必要があります。私はこのstackoverflow.com/a/28356383/1071899に
SimonBøgh

1
なぜしないfor (UIView *view in self.pageViewController.view.subviews) {
dengApro 2018

左半分と右半分のページコントロールをタップすると、ユーザーは引き続きページ間を移動できます。
MasterCarl、

57

user2159978の回答をSwift 5.1に翻訳します

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

30

@lee(@ user2159978's)ソリューションを拡張として実装する:

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

よりエレガント取得するには、あなたはそれを追加することができます: var scrollView: UIScrollView? { for view in view.subviews { if let subView = view as? UIScrollView { return subView } } return nil }
ワーグナー販売

ここのゲッターは、最後のサブビューの有効なステータスのみを返します。
Andrew Duncan

8

私はこれとしばらく戦っていますが、Jessedcの回答に続いて、自分の解決策を投稿する必要があると考えました。PageViewControllerのデータソースを削除します。

これをPgeViewControllerクラスに追加しました(ストーリーボードのページビューコントローラーにリンクされ、UIPageViewControllerとの両方を継承しますUIPageViewControllerDataSource)。

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に翻訳したい場合は:)


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 14

皆さんはこれをスクロールスタイルで理解しましたか?
Esqarrouth 2015

4

あなたがしたい場合はUIPageViewControllerスワイプすることの能力を維持するために、(スワイプなどを削除するには)その機能を使用するためにコンテンツのコントロールを可能にしながら、ちょうどオフcanCancelContentTouchesUIPageViewController

これをあなたUIPageViewControllerviewDidLoadファンクに入れてください。(迅速)

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

UIPageViewControllerは、ジェスチャを処理する自動生成サブビューがあります。これらのサブビューがコンテンツのジェスチャーをキャンセルしないようにすることができます。

から...

スワイプしてpageViewController内にあるtableViewを削除します


カーターに感謝します。私はあなたの解決策を試しました。ただし、pageViewControllerが子テーブルビューから水平スワイプアクションを追い越す場合はまだあります。ユーザーが子テーブルビューをスワイプするときに、最初にジェスチャを取得するようにテーブルビューが常に優先されるようにするにはどうすればよいですか?
David Liu

3

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

3

私はそれをこのように解決しました(Swift 4.1)

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

3

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

0

@ user3568340の回答に似ています

スウィフト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
        }
    }

0

@ user2159978の回答に感謝します。

もう少しわかりやすくします。

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

0

私が見つけた答えは私にとって非常に混乱または不完全に見えるので、ここに完全で構成可能なソリューションがあります:

ステップ1:

左右のスクロールが有効かどうかを判断する責任を各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 View Controllerは、上記のプロトコルを実装する必要があります。

ステップ2:

PVCコントローラーで、必要に応じてスクロールを無効にします。

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

class SomeViewController: UIViewController {
    // ...    
}

ステップ3:

効果的なスクロールロックメソッドを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
    }
}

ステップ4:

新しい画面に到達したときにスクロール関連の属性を更新します(画面に手動で移行する場合は、enableScrollメソッドを呼び出すことを忘れないでください):

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

0

ここでのほとんどの回答が提案するよりもはるかに単純なアプローチがあります。それはnilviewControllerBeforeおよびviewControllerAfterdataSourceコールバックで返すことです。

これにより、iOS 11以降のデバイスでのスクロールジェスチャーが無効になりますが、dataSourcepresentationIndex/ などのpresentationCountページインジケーターに使用される

また、経由のナビゲーションを無効にします。pageControl(下のドット)

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


-1

@ user2159978の応答をC#に変換します。

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

-1

(Swift 4)pageViewControllerのGestureRecognizerを削除できます。

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

あなたが拡張で好むなら:

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

そして pageViewController.removeGestureRecognizers


-1

次のように宣言します。

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

次に、次のように使用します。

scrollView?.isScrollEnabled = true //false

-1

UIPageViewControllerページコントローラーサブクラスでscrollViewが見つからないため、サブビューを列挙してaのscrollViewを見つけることはできませんでした。つまり、ジェスチャー認識機能を無効にすることですが、必要なものを無効にしないように十分注意してください。

だから私はこれを思いつきました:

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

中に入れれviewDidLoad()ば、準備は完了です!


-1
 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%働く


このコードが直面している問題が完璧に機能するかコメントしてください:/
ap00724

-1

このコントロールプロパティをUIPageViewControllerサブクラスに追加するだけです。

var isScrollEnabled = true {
    didSet {
        for case let scrollView as UIScrollView in view.subviews {
            scrollView.isScrollEnabled = isScrollEnabled
        }
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.