Wie deaktiviere ich die Wischgeste von UIPageViewController?

125

In meinem Fall UIViewControllerenthält Eltern UIPageViewControllerwelche UINavigationControllerwelche welche welche enthält UIViewController. Ich muss dem letzten Ansichts-Controller eine Wischgeste hinzufügen, aber Wischen werden so behandelt, als ob sie zum Seitenansichts-Controller gehören. Ich habe versucht, dies sowohl programmgesteuert als auch über xib zu tun, aber ohne Ergebnis.

So wie ich es verstehe, kann ich mein Ziel erst erreichen, wenn ich UIPageViewControllermit seinen Gesten fertig bin . Wie kann man dieses Problem lösen?

user2159978
quelle
7
Verwenden SiepageViewControllerObject.view.userInteractionEnabled = NO;
Srikanth
6
falsch, weil es auch seinen gesamten Inhalt blockiert. Aber ich muss nur seine Gesten blockieren
user2159978
Warum tust du das? Welches Verhalten möchten Sie von der App? Es hört sich so an, als würden Sie die Architektur überkomplizieren. Können Sie erklären, welches Verhalten Sie anstreben?
Fogmeister
Eine der Seiten in UIPageViewControllerhat UINavigationController. Der Kunde möchte den Navigationscontroller bei einer Wischgeste
öffnen
@Srikanth danke, genau das habe ich gebraucht! Meine Seitenansicht stürzte ab, als ich einen Seitenwechsel programmgesteuert auslöste und jemand ihn mit dem Wischen unterbrach. Daher musste ich das Wischen jedes Mal vorübergehend deaktivieren, wenn der Seitenwechsel im Code ausgelöst wird ...
Kuba Suder

Antworten:

262

Die dokumentierte Möglichkeit, das UIPageViewControllerScrollen zu verhindern, besteht darin, die dataSourceEigenschaft nicht zuzuweisen . Wenn Sie die Datenquelle zuweisen, wechselt sie in den "gestenbasierten" Navigationsmodus, den Sie verhindern möchten.

Ohne Datenquelle stellen Sie View Controller manuell zur Verfügung, wenn Sie die setViewControllers:direction:animated:completionMethode verwenden möchten, und sie werden bei Bedarf zwischen View Controllern verschoben.

Das Obige kann aus Apples Dokumentation von UIPageViewController (Übersicht, zweiter Absatz) abgeleitet werden:

Um die gestenbasierte Navigation zu unterstützen, müssen Sie Ihre Ansichtssteuerungen mithilfe eines Datenquellenobjekts bereitstellen.

Jessedc
quelle
1
Ich bevorzuge auch diese Lösung. Sie können die alte Datenquelle in einer Variablen speichern, die Datenquelle auf Null setzen und dann die alte Datenquelle wiederherstellen, um das Scrollen wieder zu aktivieren. Nicht so sauber, aber viel besser als die zugrunde liegende Ansichtshierarchie zu durchlaufen, um die Bildlaufansicht zu finden.
Matt R
3
Das Deaktivieren der Datenquelle verursacht Probleme, wenn Sie auf Tastaturbenachrichtigungen für Textfeld-Unteransichten reagieren. Das Durchlaufen der Unteransichten der Ansicht, um die UIScrollView zu finden, scheint zuverlässiger zu sein.
AR Younce
9
Entfernt dies nicht die Punkte am unteren Bildschirmrand? Und wenn Sie nicht wischen und keine Punkte haben, macht es wenig Sinn, einen PageViewController zu verwenden. Ich vermute, der Fragesteller möchte die Punkte behalten.
Leo Flaherty
Dies half bei der Lösung eines Problems, mit dem ich konfrontiert war, als die Seiten asynchron geladen wurden und der Benutzer eine Wischgeste ausführt. Bravo :)
Ja͢ck
1
A. R Younce ist korrekt. Wenn Sie die Datenquelle des PageViewController als Reaktion auf eine Tastaturbenachrichtigung deaktivieren (dh Sie möchten nicht, dass der Benutzer bei geöffneter Tastatur horizontal scrollt), verschwindet Ihre Tastatur sofort, wenn Sie die Datenquelle des PageViewController nicht mehr verwenden.
Micnguyen
82
for (UIScrollView *view in self.pageViewController.view.subviews) {

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

        view.scrollEnabled = NO;
    }
}
user2159978
quelle
5
Dieser behält die Seitenkontrolle, was sehr schön ist.
Marcus Adams
1
Dies ist ein großartiger Ansatz, da die Seitensteuerung (kleine Punkte) erhalten bleibt. Sie sollten sie aktualisieren, wenn Sie die Seite manuell ändern. Ich habe diese Antwort für diesen stackoverflow.com/a/28356383/1071899
Simon Bøgh
1
Warum nichtfor (UIView *view in self.pageViewController.view.subviews) {
dengApro
Bitte beachten Sie, dass Benutzer weiterhin zwischen den Seiten navigieren können, indem Sie auf das Seitensteuerelement in der linken und rechten Hälfte tippen.
MasterCarl
57

Ich übersetze die Antwort von user2159978 in Swift 5.1

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

Implementierung der Lösung von @ lee (@ user2159978) als Erweiterung:

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

Verwendung: (in UIPageViewController)

self.isPagingEnabled = false
LinusGeffarth
quelle
Um eleganter zu werden, können Sie var scrollView: UIScrollView? { for view in view.subviews { if let subView = view as? UIScrollView { return subView } } return nil }
Wagner Sales
Der Getter hier gibt nur den aktivierten Status der letzten Unteransicht zurück.
Andrew Duncan
8

Ich kämpfe jetzt schon eine Weile dagegen und dachte, ich sollte meine Lösung veröffentlichen, basierend auf Jessedcs Antwort; Entfernen der PageViewController-Datenquelle.

Ich habe dies meiner PgeViewController-Klasse hinzugefügt (verknüpft mit meinem Seitenaufruf-Controller im Storyboard, erbt beide UIPageViewControllerund 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
    }
}

Dies kann dann aufgerufen werden, wenn jede Unteransicht angezeigt wird (in diesem Fall, um sie zu deaktivieren).

override func viewDidAppear(animated: Bool) {
    PgeViewController.enable(false)
}

Ich hoffe, das hilft jemandem, es ist nicht so sauber, wie ich es gerne hätte, aber es fühlt sich nicht zu hackig an usw.

EDIT: Wenn jemand dies in Objective-C übersetzen möchte, bitte :)

Jamie Robinson
quelle
8

Bearbeiten : Diese Antwort funktioniert nur für den Seitenrollstil. Jessedcs Antwort ist weitaus besser: Funktioniert unabhängig vom Stil und stützt sich auf dokumentiertes Verhalten .

UIPageViewController macht eine Reihe von Gestenerkennern verfügbar, mit denen Sie sie deaktivieren können:

// myPageViewController is your UIPageViewController instance
for (UIGestureRecognizer *recognizer in myPageViewController.gestureRecognizers) {
    recognizer.enabled = NO;
}
Austin
quelle
8
Haben Sie Ihren Code getestet? myPageViewController.gestureRecognizers ist immer leer
user2159978
11
Es scheint, dass Ihre Lösung für den
Seitenrollstil
8
Funktioniert nicht für horizontales Scrollen. Der zurückgegebene Wert ist immer ein leeres Array.
Khanh Nguyen
habt ihr das für den Scroll-Stil herausgefunden?
Esqarrouth
4

Wenn Sie möchten, dass Sie UIPageViewControllerweiterhin wischen können, während Ihre Inhaltssteuerelemente ihre Funktionen verwenden können (Wischen zum Löschen usw.), schalten Sie einfach canCancelContentTouchesin aus UIPageViewController.

Setzen Sie dies in Ihre Funktion UIPageViewControllerein viewDidLoad. (Schnell)

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

Das UIPageViewControllerhat eine automatisch generierte Unteransicht, die die Gesten verarbeitet. Wir können verhindern, dass diese Unteransichten Inhaltsgesten abbrechen.

Von...

Wischen Sie zum Löschen in einer Tabellenansicht, die sich in einem pageViewController befindet

Carter Medlin
quelle
Danke Carter. Ich habe deine Lösung ausprobiert. Es gibt jedoch immer noch Zeiten, in denen pageViewController die horizontale Wischaktion aus der untergeordneten Tabellenansicht entfernt. Wie kann ich sicherstellen, dass beim Wischen des Benutzers in der untergeordneten Tabellenansicht die Tabellenansicht immer priorisiert wird, um die Geste zuerst zu erhalten?
David Liu
3

Eine nützliche Erweiterung UIPageViewControllerzum Aktivieren und Deaktivieren von Swipe.

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
            }
        }
    }
}
jbustamante
quelle
3

Ich habe es so gelöst (Swift 4.1)

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

Schneller Weg für @lee Antwort

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
    }
}
Szymon W.
quelle
0

Ähnlich wie bei @ user3568340 Antwort

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
        }
    }
Emmanouil Nicolas
quelle
0

Vielen Dank an die Antwort von @ user2159978.

Ich mache es etwas verständlicher.

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

Die Antworten, die ich gefunden habe, sehen für mich sehr verwirrend oder unvollständig aus. Hier ist eine vollständige und konfigurierbare Lösung:

Schritt 1:

Geben Sie jedem Ihrer PVC-Elemente die Verantwortung, festzustellen, ob das Scrollen nach links und rechts aktiviert ist oder nicht.

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

Jeder Ihrer PVC-Ansichtscontroller sollte das obige Protokoll implementieren.

Schritt 2:

Deaktivieren Sie in Ihren PVC-Controllern bei Bedarf den Bildlauf:

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

class SomeViewController: UIViewController {
    // ...    
}

Schritt 3:

Fügen Sie Ihrem PVC die effektiven Scroll-Lock-Methoden hinzu:

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

Schritt 4:

Aktualisieren Sie die scrollbezogenen Attribute, wenn Sie einen neuen Bildschirm erreichen (wenn Sie manuell zu einem Bildschirm wechseln, vergessen Sie nicht, die enableScroll-Methode aufzurufen):

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")
    }
}
michael-martinez
quelle
0

Es gibt eine viel einfachere Methode als die meisten Antworten hier vorschlagen, die Rückkehr ist nilin den viewControllerBeforeund viewControllerAfterDatasource - Rückrufe.

Dies deaktiviert die Bildlaufgeste auf iOS 11+ Geräten, während die Möglichkeit beibehalten wird, die dataSource(für Dinge wie die presentationIndex/ presentationCountfür die Seitenanzeige verwendet) zu verwenden.

Es deaktiviert auch die Navigation über. die pageControl(die Punkte unten)

extension MyPageViewController: UIPageViewControllerDataSource {
    func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
        return nil
    }
    
    func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
        return nil
    }
}
Claus Jørgensen
quelle
0

pageViewController.view.isUserInteractionEnabled = false

gutte
quelle
-1

Übersetzen der Antwort von @ user2159978 auf C #:

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

(Swift 4) Sie können gestureRecognizers von Ihrem pageViewController entfernen:

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

Wenn Sie eine Erweiterung bevorzugen:

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

und pageViewController.removeGestureRecognizers

Miguel
quelle
-1

Erklären Sie es so:

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

Dann benutze es so:

scrollView?.isScrollEnabled = true //false
Bartłomiej Semańczyk
quelle
-1

Das Auflisten der Unteransichten, um die Bildlaufansicht von zu finden, UIPageViewControllerhat bei mir nicht funktioniert, da ich in meiner Seitencontroller-Unterklasse keine Bildlaufansicht finden kann. Ich dachte also daran, die Gestenerkenner zu deaktivieren, aber vorsichtig genug, um die erforderlichen nicht zu deaktivieren.

Also habe ich mir Folgendes ausgedacht:

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

Legen Sie das in die viewDidLoad()und Sie sind fertig!

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

    }
}

Fügen Sie dies in Ihre pageviewcontroller-Klasse ein. 100% arbeiten

ap00724
quelle
Bitte kommentieren Sie, mit welchem ​​Problem Sie konfrontiert sind. Dieser Code funktioniert einwandfrei: /
ap00724
-1

Fügen Sie diese Steuereigenschaft einfach in Ihre UIPageViewController- Unterklasse ein:

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