Come posso disabilitare il gesto di scorrimento di UIPageViewController?


125

Nel mio caso genitore UIViewControllercontiene UIPageViewControllerquali contiene UINavigationControllerquali contengono UIViewController. Devo aggiungere un gesto di scorrimento all'ultimo controller di visualizzazione, ma i passaggi vengono gestiti come se appartenessero al controller di visualizzazione della pagina. Ho provato a farlo sia a livello di programmazione che tramite xib ma senza risultati.

Quindi, a quanto ho capito, non posso raggiungere il mio obiettivo fino a quando non UIPageViewControllergestirà i suoi gesti. Come risolvere questo problema?


7
UsapageViewControllerObject.view.userInteractionEnabled = NO;
Srikanth,

6
errato, perché blocca anche tutto il suo contenuto. Ma devo bloccare solo i suoi gesti
user2159978

Perché stai facendo questo? Quale comportamento desideri dall'app? Sembra che tu stia complicando eccessivamente l'architettura. Puoi spiegare a quale comportamento stai mirando.
Nebbia

Una delle pagine UIPageViewControllerha UINavigationController. Il cliente desidera far apparire il controller di navigazione sul gesto di scorrimento (se possibile), ma invece il controller di visualizzazione della pagina gestisce i passaggi
user2159978

@Srikanth grazie, è esattamente quello di cui avevo bisogno! La visualizzazione della mia pagina si arrestava in modo anomalo quando ho attivato una modifica di pagina a livello di programmazione e qualcuno lo ha interrotto con lo scorrimento, quindi ho dovuto disabilitare temporaneamente lo scorrimento ogni volta che la modifica della pagina è attivata nel codice ...
Kuba Suder,

Risposte:


262

Il modo documentato per impedire lo UIPageViewControllerscorrimento è di non assegnare la dataSourceproprietà. Se si assegna l'origine dati, questa passerà alla modalità di navigazione "gestuale", che è ciò che si sta tentando di prevenire.

Senza un'origine dati fornisci manualmente i controller di visualizzazione quando vuoi con il setViewControllers:direction:animated:completionmetodo e si sposterà tra i controller di visualizzazione su richiesta.

Quanto sopra può essere dedotto dalla documentazione Apple di UIPageViewController (Panoramica, secondo paragrafo):

Per supportare la navigazione basata sui gesti, è necessario fornire i controller di visualizzazione utilizzando un oggetto origine dati.


1
Preferisco anche questa soluzione. È possibile salvare l'origine dati precedente in una variabile, impostare l'origine dati su zero e quindi ripristinare l'origine dati precedente per riattivare lo scorrimento. Non molto pulito, ma molto meglio che attraversare la gerarchia della vista sottostante per trovare la vista di scorrimento.
Matt R

3
La disabilitazione dell'origine dati causa problemi quando lo si fa in risposta alle notifiche da tastiera per le sottoview dei campi di testo. Scorrere le visualizzazioni secondarie della vista per trovare UIScrollView sembra essere più affidabile.
AR Younce

9
Questo non rimuove i punti nella parte inferiore dello schermo? E se non hai lo scorrimento e nessun punto, non ha molto senso usare un PageViewController. Immagino che chi chiede vuole conservare i punti.
Leo Flaherty,

Ciò ha aiutato a risolvere un problema che stavo affrontando quando le pagine vengono caricate in modo asincrono e l'utente fa un gesto di scorrimento. Bravo :)
Ja͢ck

1
A. R Younce è corretta. Se si sta disabilitando l'origine dati di PageViewController in risposta a una notifica della tastiera (ovvero: non si desidera che l'utente scorra orizzontalmente quando la tastiera è in alto), la tastiera scomparirà immediatamente quando si annulla l'origine dati di PageViewController.
micnguyen,

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

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

        view.scrollEnabled = NO;
    }
}

5
Questo mantiene il controllo della pagina, il che è carino.
Marcus Adams,

1
Questo è un ottimo approccio poiché mantiene il controllo della pagina (piccoli punti). Ti consigliamo di aggiornarli quando si modifica manualmente la pagina. Ho usato questa risposta per questo stackoverflow.com/a/28356383/1071899
Simon Bøgh

1
Perché nofor (UIView *view in self.pageViewController.view.subviews) {
dengApro

Tieni presente che gli utenti potranno comunque navigare tra le pagine toccando il controllo della pagina nella metà sinistra e destra.
MasterCarl

57

Traduco la risposta dell'utente2159978 a Swift 5.1

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

30

Implementazione della soluzione di @ lee (@ user2159978) come estensione:

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

Utilizzo: (in UIPageViewController)

self.isPagingEnabled = false

Per diventare più elegante, puoi aggiungere che: var scrollView: UIScrollView? { for view in view.subviews { if let subView = view as? UIScrollView { return subView } } return nil }
Vendite Wagner

Il getter qui restituisce solo lo stato abilitato dell'ultima vista secondaria.
Andrew Duncan,

8

Ho combattuto per un po 'di tempo e ho pensato di pubblicare la mia soluzione, in seguito alla risposta di Jessedc; rimuovendo l'origine dati di PageViewController.

Ho aggiunto questo alla mia classe PgeViewController (collegato al mio controller di visualizzazione di pagina nello storyboard, eredita entrambi UIPageViewControllere 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
    }
}

Questo può quindi essere chiamato quando appare una vista secondaria (in questo caso per disabilitarla);

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

Spero che questo aiuti qualcuno, non è pulito come mi piacerebbe ma non mi sento troppo confuso ecc.

EDIT: Se qualcuno vuole tradurre questo in Objective-C, per favore fallo :)


8

Modifica : questa risposta funziona solo per lo stile di arricciatura della pagina. La risposta di Jessedc è di gran lunga migliore: funziona indipendentemente dallo stile e si basa su comportamenti documentati .

UIPageViewController espone la sua gamma di riconoscitori di gesti, che è possibile utilizzare per disabilitarli:

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

8
Hai testato il tuo codice? myPageViewController.gestureRecognizers è sempre vuoto
user2159978

11
sembra che la tua soluzione funzioni per lo stile di arricciatura della pagina, ma nella mia app utilizzo lo stile di visualizzazione scorrimento
user2159978

8
Non funziona per lo scorrimento orizzontale. Il valore restituito è sempre un array vuoto.
Khanh Nguyen,

ragazzi lo avete capito per lo stile scroll?
Esqarrouth,

4

Se desideri UIPageViewControllermantenere la sua capacità di scorrere, consentendo al contempo ai controlli dei contenuti di utilizzare le loro funzionalità (Scorri per eliminare, ecc.), Disattiva canCancelContentTouchesilUIPageViewController .

Mettete questo nella vostra UIPageViewController's viewDidLoadfunc. (Swift)

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

Il UIPageViewControllerha una visualizzazione secondaria generato automaticamente che gestisce i gesti. Siamo in grado di impedire a queste visualizzazioni secondarie di annullare i gesti dei contenuti.

A partire dal...

Scorri per eliminare su un tableView che si trova all'interno di un pageViewController


Grazie Carter. Ho provato la tua soluzione. Tuttavia, ci sono ancora volte in cui pageViewController sostituisce l'azione di scorrimento orizzontale lontano dalla vista tabella figlio. Come posso assicurarmi che quando l'utente passa sulla vista tabella figlio, la vista tabella è sempre prioritaria per ottenere prima il gesto?
David Liu,

3

Un'utile estensione UIPageViewControllerper abilitare e disabilitare lo scorrimento.

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

L'ho risolto in questo modo (Swift 4.1)

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

3

Modo veloce per la risposta @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

Simile alla risposta @utente3568340

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

0

Grazie alla risposta di @ user2159978.

Lo rendo un po 'più comprensibile.

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

0

Le risposte che ho trovato mi sembrano molto confuse o incomplete, quindi ecco una soluzione completa e configurabile:

Passo 1:

Dai a ciascuno dei tuoi elementi in PVC la responsabilità di dire se lo scorrimento sinistro e destro sono abilitati o meno.

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

Ognuno dei controller di visualizzazione PVC deve implementare il protocollo sopra.

Passo 2:

Nei controller in PVC, disabilitare lo scorrimento, se necessario:

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

class SomeViewController: UIViewController {
    // ...    
}

Passaggio 3:

Aggiungi i metodi di blocco scorrimento efficaci al tuo 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
    }
}

Step 4:

Aggiorna gli attributi relativi allo scorrimento quando si raggiunge una nuova schermata (se si passa a una schermata manualmente, non dimenticare di chiamare il metodo 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

Esiste un approccio molto più semplice di quello che la maggior parte delle risposte suggerisce, che è quello di restituire nili callback di viewControllerBeforee viewControllerAfterdataSource.

Ciò disabilita il gesto di scorrimento sui dispositivi iOS 11+, mantenendo la possibilità di utilizzare il dataSource(per cose come il presentationIndex/presentationCount usato per l'indicatore di pagina)

Disabilita anche la navigazione tramite. il pageControl(i punti in fondo)

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

0

pageViewController.view.isUserInteractionEnabled = false


-1

Traduzione della risposta di @ user2159978 a C #:

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

-1

(Swift 4) Puoi rimuovere gestureRecognizer dalla tua pagina ViewController:

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

Se preferisci in estensione:

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

e pageViewController.removeGestureRecognizers


-1

Dichiaralo così:

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

Quindi usalo in questo modo:

scrollView?.isScrollEnabled = true //false

-1

Enumerazione delle viste secondarie per trovare scrollView di a UIPageViewController non ha funzionato per me, poiché non riesco a trovare scrollView nella sottoclasse del controller di pagina. Quindi quello che ho pensato di fare è disabilitare i riconoscitori di gesti, ma abbastanza attento da non disabilitare quelli necessari.

Quindi ho pensato a questo:

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

Mettilo dentro viewDidLoad()e sei pronto!


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

    }
}

Aggiungi questo nella tua classe pageviewcontroller. 100% funzionante


commenta quale problema stai
riscontrando

-1

basta aggiungere questa proprietà di controllo nella sottoclasse UIPageViewController :

var isScrollEnabled = true {
    didSet {
        for case let scrollView as UIScrollView in view.subviews {
            scrollView.isScrollEnabled = isScrollEnabled
        }
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.