¿Cómo desactivo el gesto de deslizar de UIPageViewController?

125

En mi caso, el padre UIViewControllercontiene UIPageViewControllercuál contiene UINavigationControllercuál contiene UIViewController. Necesito agregar un gesto de deslizamiento al último controlador de vista, pero los deslizamientos se manejan como si pertenecieran al controlador de vista de página. Traté de hacer esto tanto programáticamente como a través de xib pero sin resultado.

Entonces, según tengo entendido, no puedo lograr mi objetivo hasta que UIPageViewControllermaneje sus gestos. ¿Cómo resolver este problema?

usuario2159978
fuente
77
UsopageViewControllerObject.view.userInteractionEnabled = NO;
Srikanth
66
incorrecto, porque también bloquea todo su contenido. Pero solo necesito bloquear sus gestos
user2159978
¿Por qué estás haciendo esto? ¿Qué comportamiento quieres de la aplicación? Parece que estás complicando demasiado la arquitectura. ¿Puede explicar a qué comportamiento apunta?
Fogmeister
Una de las páginas en UIPageViewControllertiene UINavigationController. El cliente quiere hacer estallar el controlador de navegación en el gesto de deslizar (si es posible), pero el controlador de vista de página maneja los deslizadores en su lugar
user2159978
@Srikanth gracias, ¡eso es exactamente lo que necesitaba! La vista de mi página se bloqueaba cuando activé un cambio de página programáticamente y alguien lo interrumpió al deslizar, así que tuve que desactivarlo temporalmente cada vez que se activa el cambio de página en el código ...
Kuba Suder

Respuestas:

262

La forma documentada de evitar el UIPageViewControllerdesplazamiento es no asignar la dataSourcepropiedad. Si asigna la fuente de datos, pasará al modo de navegación 'basado en gestos', que es lo que está tratando de evitar.

Sin una fuente de datos, usted proporciona manualmente los controladores de vista cuando lo desee con el setViewControllers:direction:animated:completionmétodo y se moverá entre los controladores de vista a pedido.

Lo anterior se puede deducir de la documentación de Apple de UIPageViewController (Descripción general, segundo párrafo):

Para admitir la navegación basada en gestos, debe proporcionar sus controladores de vista utilizando un objeto de origen de datos.

Jessedc
fuente
1
Prefiero esta solución también. Puede guardar el origen de datos antiguo en una variable, establecer el origen de datos en nulo y luego restaurar el origen de datos antiguo para volver a habilitar el desplazamiento. No tan limpio, pero mucho mejor que atravesar la jerarquía de vista subyacente para encontrar la vista de desplazamiento.
Matt R
3
Deshabilitar la fuente de datos causa problemas al hacerlo en respuesta a las notificaciones del teclado para subvistas de campo de texto. Iterar sobre las subvistas de la vista para encontrar el UIScrollView parece ser más confiable.
AR Younce
9
¿No elimina esto los puntos en la parte inferior de la pantalla? Y si no tiene deslizamiento ni puntos, no tiene mucho sentido usar un PageViewController. Supongo que el autor de la pregunta quiere retener los puntos.
Leo Flaherty
Esto ayudó a resolver un problema que estaba enfrentando cuando las páginas se cargan de forma asincrónica y el usuario hace un gesto de deslizamiento. Bravo :)
Ja͢ck
1
A. R Younce es correcto. Si está deshabilitando la fuente de datos de PageViewController en respuesta a una notificación del teclado (es decir: no desea que el usuario se desplace horizontalmente cuando el teclado está arriba), entonces su teclado desaparecerá inmediatamente cuando cierre la fuente de datos de PageViewController.
micnguyen
82
for (UIScrollView *view in self.pageViewController.view.subviews) {

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

        view.scrollEnabled = NO;
    }
}
usuario2159978
fuente
55
Este mantiene el control de la página, lo cual es bueno.
Marcus Adams
1
Este es un gran enfoque ya que mantiene el control de la página (pequeños puntos). Deberá actualizarlos cuando haga que la página cambie manualmente.
Usé
1
¿Por qué no?for (UIView *view in self.pageViewController.view.subviews) {
dengApro
Tenga en cuenta que los usuarios aún podrán navegar entre páginas tocando el control de página en la mitad izquierda y derecha.
MasterCarl
57

Traduzco la respuesta de user2159978 a Swift 5.1

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

Implementando la solución de @ lee (@ user2159978) como una extensión:

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

Uso: (en UIPageViewController)

self.isPagingEnabled = false
LinusGeffarth
fuente
Para ser más elegante, puede agregar eso: var scrollView: UIScrollView? { for view in view.subviews { if let subView = view as? UIScrollView { return subView } } return nil }
Wagner Sales el
El getter aquí solo devuelve el estado habilitado de la última subvista.
Andrew Duncan
8

He estado luchando contra esto por un tiempo y pensé que debería publicar mi solución, siguiendo la respuesta de Jessedc; eliminando el origen de datos de PageViewController.

Agregué esto a mi clase PgeViewController (vinculado a mi controlador de vista de página en el guión gráfico, hereda ambos UIPageViewControllery 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
    }
}

Esto se puede invocar cuando aparece cada vista secundaria (en este caso para deshabilitarla);

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

Espero que esto ayude a alguien, no es tan limpio como me gustaría, pero no se siente demasiado hacky, etc.

EDITAR: si alguien quiere traducir esto a Objective-C, por favor hazlo :)

Jamie Robinson
fuente
8

Editar : esta respuesta funciona solo para el estilo de curva de página. La respuesta de Jessedc es mucho mejor: funciona independientemente del estilo y se basa en un comportamiento documentado .

UIPageViewController expone su conjunto de reconocedores de gestos, que puede usar para deshabilitarlos:

// myPageViewController is your UIPageViewController instance
for (UIGestureRecognizer *recognizer in myPageViewController.gestureRecognizers) {
    recognizer.enabled = NO;
}
Austin
fuente
8
¿Has probado tu código? myPageViewController.gestureRecognizers siempre está vacío
user2159978
11
parece que su solución funciona para el estilo de curl de página, pero en mi aplicación utilizo el estilo de vista de desplazamiento
user2159978
8
No funciona para desplazamiento horizontal. El valor devuelto siempre es una matriz vacía.
Khanh Nguyen
¿Ustedes resolvieron esto para el estilo de desplazamiento?
Esqarrouth
4

Si desea UIPageViewControllermantener su capacidad de deslizar, mientras permite que sus controles de contenido usen sus funciones (deslizar para eliminar, etc.), simplemente desactívelo canCancelContentTouchesen UIPageViewController.

Poner esto en su UIPageViewController's viewDidLoadfunc. (Rápido)

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

El UIPageViewControllertiene una subvista autogenerado que se encarga de los gestos. Podemos evitar que estas subvistas cancelen los gestos de contenido.

De...

Deslice para eliminar en una tableView que está dentro de un pageViewController

Carter Medlin
fuente
Gracias Carter He probado tu solución. Sin embargo, todavía hay momentos en que pageViewController elimina la acción de deslizamiento horizontal de la vista de tabla secundaria. ¿Cómo puedo asegurarme de que cuando el usuario desliza la vista de la tabla secundaria, la vista de la tabla siempre tiene prioridad para obtener el gesto primero?
David Liu
3

Una extensión útil UIPageViewControllerpara habilitar y deshabilitar deslizar.

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
fuente
3

Lo resolví así (Swift 4.1)

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

Manera rápida para la respuesta @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
    }
}
Szymon W
fuente
0

Respuesta similar a @ 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
        }
    }
Emmanouil Nicolas
fuente
0

Gracias a la respuesta de @ user2159978.

Lo hago un poco más comprensible.

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

Las respuestas que encontré me parecen muy confusas o incompletas, así que aquí hay una solución completa y configurable:

Paso 1:

Dele a cada uno de sus elementos de PVC la responsabilidad de determinar si el desplazamiento hacia la izquierda y la derecha están habilitados o no.

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

Cada uno de sus controladores de vista de PVC debe implementar el protocolo anterior.

Paso 2:

En sus controladores de PVC, desactive el desplazamiento si es necesario:

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

class SomeViewController: UIViewController {
    // ...    
}

Paso 3:

Agregue los métodos efectivos de bloqueo de desplazamiento a su 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
    }
}

Paso 4:

Actualice los atributos relacionados con el desplazamiento cuando llegue a una nueva pantalla (si realiza la transición manualmente a alguna pantalla, no olvide llamar al método 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")
    }
}
michael-martinez
fuente
0

Hay un enfoque mucho más simple que la mayoría de las respuestas aquí sugeridas, que es regresar nilen las devoluciones de llamada viewControllerBeforey viewControllerAfterdataSource.

Esto deshabilita el gesto de desplazamiento en dispositivos iOS 11+, al tiempo que mantiene la posibilidad de usar dataSource(para cosas como presentationIndex/ presentationCountutilizado para el indicador de página)

También deshabilita la navegación a través de. los pageControl(los puntos en la parte inferior)

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
fuente
0

pageViewController.view.isUserInteractionEnabled = false

gutte
fuente
-1

Traduciendo la respuesta de @ user2159978 a C #:

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

(Swift 4) Puede eliminar los gestos de reconocimiento de su pageViewController:

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

Si lo prefieres en extensión:

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

y pageViewController.removeGestureRecognizers

Miguel
fuente
-1

Declararlo así:

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

Entonces úsalo así:

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

Enumerar las subvistas para encontrar el scrollView de un UIPageViewControllerno funcionó para mí, ya que no puedo encontrar ningún scrollView en mi subclase de controlador de página. Entonces, lo que pensé hacer es deshabilitar los reconocedores de gestos, pero lo suficientemente cuidadoso como para no deshabilitar los necesarios.

Entonces se me ocurrió esto:

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

¡Pon eso dentro viewDidLoad()y listo!

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

    }
}

Agregue esto en su clase pageviewcontroller. 100% trabajando

ap00724
fuente
comente qué problema está enfrentando este código funciona perfectamente: /
ap00724
-1

simplemente agregue esta propiedad de control en su subclase UIPageViewController :

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