Jak wyłączyć gest przesuwania UIPageViewController?


125

W moim przypadku rodzic UIViewControllerzawiera UIPageViewControllerktóry zawiera UINavigationControllerktóry zawieraUIViewController . Muszę dodać gest przesunięcia do ostatniego kontrolera widoku, ale przesunięcia są obsługiwane tak, jakby należały do ​​kontrolera widoku strony. Próbowałem to zrobić zarówno programowo, jak i przez xib, ale bez rezultatu.

Więc jak rozumiem, nie mogę osiągnąć celu, dopóki nie poradzę UIPageViewControllersobie z jego gestami. Jak rozwiązać ten problem?


7
UżyjpageViewControllerObject.view.userInteractionEnabled = NO;
Srikanth

6
niepoprawne, ponieważ blokuje również całą jego zawartość. Ale muszę zablokować tylko jego gesty
user2159978

Dlaczego to robisz? Jakiego zachowania chcesz od aplikacji? Wygląda na to, że zbytnio komplikujesz architekturę. Czy możesz wyjaśnić, do jakiego zachowania dążysz.
Fogmeister

Jedna ze stron w UIPageViewControllerma UINavigationController. Klient chce
uruchomić

@Srikanth dzięki, właśnie tego potrzebowałem! Mój widok strony ulegał awarii, gdy uruchomiłem programową zmianę strony i ktoś przerwał ją przeciąganiem, więc musiałem tymczasowo wyłączać przesuwanie za każdym razem, gdy zmiana strony jest uruchamiana w kodzie ...
Kuba Suder

Odpowiedzi:


262

Udokumentowanym sposobem zapobiegania UIPageViewControllerprzewijaniu jest nieprzypisywanie dataSourcewłaściwości. Jeśli przypiszesz źródło danych, przejdzie ono do trybu nawigacji opartego na gestach, czemu próbujesz zapobiec.

Bez źródła danych ręcznie udostępniasz kontrolery widoku, kiedy chcesz setViewControllers:direction:animated:completion metody, a na żądanie będą one przenoszone między kontrolerami widoku.

Powyższe można wywnioskować z dokumentacji firmy Apple dotyczącej UIPageViewController (Omówienie, drugi akapit):

Aby obsługiwać nawigację opartą na gestach, należy zapewnić kontrolery widoku przy użyciu obiektu źródła danych.


1
Ja też wolę to rozwiązanie. Możesz zapisać stare źródło danych w zmiennej, ustawić źródło danych na zero, a następnie przywrócić stare źródło danych, aby ponownie włączyć przewijanie. Nie wszystko to jest takie przejrzyste, ale znacznie lepsze niż przechodzenie przez podstawową hierarchię widoków w celu znalezienia widoku przewijania.
Matt R

3
Wyłączenie źródła danych powoduje problemy w odpowiedzi na powiadomienia z klawiatury dotyczące podglądów podrzędnych pól tekstowych. Iterowanie po widokach podrzędnych widoku w celu znalezienia UIScrollView wydaje się być bardziej niezawodne.
AR Younce

9
Czy to nie usuwa kropek na dole ekranu? A jeśli nie masz przesuwania ani kropek, nie ma sensu używać PageViewController w ogóle. Domyślam się, że pytający chce zachować kropki.
Leo Flaherty

Pomogło to rozwiązać problem, z którym miałem do czynienia, gdy strony są ładowane asynchronicznie, a użytkownik wykonuje gest machnięcia. Brawo :)
Ja͢ck

1
A. R Younce ma rację. Jeśli wyłączysz źródło danych PageViewController w odpowiedzi na powiadomienie z klawiatury (tj. Nie chcesz, aby użytkownik przewijał w poziomie, gdy klawiatura jest uniesiona), twoja klawiatura natychmiast zniknie, gdy wyzerujesz źródło danych PageViewController.
micnguyen

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

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

        view.scrollEnabled = NO;
    }
}

5
Ten zachowuje kontrolę nad stroną, co jest miłe.
Marcus Adams

1
To świetne podejście, ponieważ zachowuje kontrolę nad stroną (małe kropki). Będziesz chciał je zaktualizować podczas ręcznego wprowadzania zmian na stronie. Użyłem tej odpowiedzi dla tego stackoverflow.com/a/28356383/1071899
Simon Bøgh

1
Dlaczego niefor (UIView *view in self.pageViewController.view.subviews) {
dengApro

Należy pamiętać, że użytkownicy nadal będą mogli nawigować między stronami, dotykając kontrolki strony w lewej i prawej połowie.
MasterCarl

57

Tłumaczę odpowiedź user2159978 na Swift 5.1

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

30

Wdrażanie rozwiązania @ lee's (@ user2159978's) jako rozszerzenia:

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

Użycie: (w UIPageViewController)

self.isPagingEnabled = false

Aby uzyskać bardziej elegancki wygląd, możesz dodać, że: var scrollView: UIScrollView? { for view in view.subviews { if let subView = view as? UIScrollView { return subView } } return nil }
Sprzedaż Wagner

Tutaj metoda pobierająca zwraca tylko włączony stan ostatniego widoku podrzędnego.
Andrew Duncan

8

Walczę z tym już od jakiegoś czasu i pomyślałem, że powinienem opublikować swoje rozwiązanie, kontynuując odpowiedź Jessedc; usuwanie źródła danych PageViewController.

Dodałem to do mojej klasy PgeViewController (połączonej z moim kontrolerem widoku strony w scenorysie, dziedziczy oba UIPageViewControlleri 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
    }
}

Można to następnie wywołać, gdy pojawi się każdy widok podrzędny (w tym przypadku, aby go wyłączyć);

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

Mam nadzieję, że to komuś pomoże, nie jest tak czyste, jak bym chciał, ale nie wydaje się zbyt hacky itp.

EDYCJA: Jeśli ktoś chce przetłumaczyć to na Objective-C, zrób :)


8

Edytuj : ta odpowiedź działa tylko w przypadku stylu zawinięcia strony. Odpowiedź Jessedc jest znacznie lepsza: działa niezależnie od stylu i opiera się na udokumentowanym zachowaniu .

UIPageViewController ujawnia swój zestaw aparatów do rozpoznawania gestów, których można użyć do ich wyłączenia:

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

8
Czy przetestowałeś swój kod? myPageViewController.gestureRecognizers jest zawsze puste
user2159978

11
wygląda na to, że Twoje rozwiązanie działa w przypadku stylu zawijania strony, ale w mojej aplikacji używam stylu widoku przewijania
user2159978

8
Nie działa przy przewijaniu w poziomie. Zwracana wartość jest zawsze pustą tablicą.
Khanh Nguyen

czy doszliście do tego w stylu przewijania?
Esqarrouth

4

Jeśli chcesz UIPageViewControllerzachować możliwość przesuwania, jednocześnie zezwalając kontrolkom zawartości na korzystanie z ich funkcji (przesuń, aby usunąć itp.), Po prostu wyłącz canCancelContentTouchesw UIPageViewController.

Umieścić to w swoim UIPageViewController„s viewDidLoadfunc. (Szybki)

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

Plik UIPageViewControllerMa automatycznie wygenerowane podrzędny, który obsługuje gesty. Możemy uniemożliwić tym podglądom anulowanie gestów treści.

Z...

Przesuń, aby usunąć w tableView, który znajduje się w pageViewController


Dzięki Carter. Wypróbowałem twoje rozwiązanie. Jednak nadal istnieją sytuacje, w których pageViewController zastępuje akcję przesunięcia poziomego poza widokiem tabeli podrzędnej. Jak mogę się upewnić, że kiedy użytkownik przesuwa palcem po podrzędnym widoku tabeli, widok tabeli ma zawsze priorytet, aby najpierw uzyskać gest?
David Liu,

3

Przydatne rozszerzenie UIPageViewControllerdo włączania i wyłączania machnięcia.

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

Rozwiązałem to w ten sposób (Swift 4.1)

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

3

Szybki sposób na odpowiedź @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

Podobnie jak @ user3568340 answer

Szybki 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

Dzięki odpowiedzi @ user2159978.

Sprawiam, że jest to trochę bardziej zrozumiałe.

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

0

Odpowiedzi, które znalazłem, wydają mi się bardzo zagmatwane lub niekompletne, więc oto kompletne i konfigurowalne rozwiązanie:

Krok 1:

Daj każdemu z elementów PVC odpowiedzialność za określenie, czy przewijanie w lewo i w prawo jest włączone, czy nie.

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

Każdy z kontrolerów widoku PVC powinien implementować powyższy protokół.

Krok 2:

W kontrolerach PVC wyłącz przewijanie, jeśli to konieczne:

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

class SomeViewController: UIViewController {
    // ...    
}

Krok 3:

Dodaj efektywne metody blokady przewijania do swojego 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
    }
}

Krok 4:

Zaktualizuj atrybuty związane z przewijaniem po przejściu do nowego ekranu (jeśli przejdziesz do jakiegoś ekranu ręcznie, nie zapomnij wywołać metody 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

Istnieje znacznie prostsze podejście, niż sugeruje większość odpowiedzi, które polega na zwróceniu nilw wywołaniach zwrotnych viewControllerBeforei viewControllerAfterźródła danych.

Spowoduje to wyłączenie gestu przewijania na urządzeniach z systemem iOS 11+, przy jednoczesnym zachowaniu możliwości używania dataSource(np. presentationIndex/presentationCount Używany jako wskaźnik strony)

Wyłącza również nawigację za pośrednictwem. the pageControl(kropki na dole)

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

Tłumaczenie odpowiedzi @ user2159978 na C #:

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

-1

(Swift 4) Możesz usunąć gestyRecognizers swojej stronyViewController:

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

Jeśli wolisz w przedłużeniu:

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

i pageViewController.removeGestureRecognizers


-1

Zadeklaruj to w ten sposób:

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

Następnie użyj tego w ten sposób:

scrollView?.isScrollEnabled = true //false

-1

Wyliczenie podglądów podrzędnych w celu znalezienia scrollView a UIPageViewControllernie działa dla mnie, ponieważ nie mogę znaleźć żadnego scrollView w mojej podklasie kontrolera strony. Pomyślałem więc o wyłączeniu funkcji rozpoznawania gestów, ale na tyle ostrożnie, aby nie wyłączać niezbędnych.

Więc wymyśliłem to:

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

Umieść to w środku viewDidLoad()i gotowe!


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

    }
}

Dodaj to do swojej klasy pageviewcontroller. 100% sprawne


proszę o komentarz, z jakim problemem masz do czynienia ten kod działa idealnie: /
ap00724

-1

po prostu dodaj tę właściwość kontrolną do swojej podklasy UIPageViewController :

var isScrollEnabled = true {
    didSet {
        for case let scrollView as UIScrollView in view.subviews {
            scrollView.isScrollEnabled = isScrollEnabled
        }
    }
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.