Как отключить жест смахивания UIPageViewController?


125

В моем случае parent UIViewControllercontains, UIPageViewControllerкоторый содержит, UINavigationControllerкоторый содержит UIViewController. Мне нужно добавить жест смахивания к последнему контроллеру представления, но смахивания обрабатываются так, как будто они принадлежат контроллеру представления страницы. Я пытался сделать это как программно, так и через xib, но безрезультатно.

Итак, насколько я понимаю, я не могу достичь своей цели, пока не UIPageViewControllerсправлюсь с ее жестами. Как решить эту проблему?


7
UsepageViewControllerObject.view.userInteractionEnabled = NO;
— Srikanth

6
неверно, потому что он также блокирует все его содержимое. Но мне нужно заблокировать только его жесты
— user2159978 03

Почему вы это делаете? Какого поведения вы хотите от приложения? Похоже, вы слишком усложняете архитектуру. Можете ли вы объяснить, к какому поведению вы стремитесь?
— Fogmeister 03

Одна из страниц в UIPageViewControllerрубрике UINavigationController. Заказчик хочет, чтобы контроллер навигации отображался жестом смахивания (если это возможно), но вместо этого обрабатывает контроллер просмотра страницы
— user2159978

@Srikanth, спасибо, это именно то, что мне нужно! При просмотре моей страницы происходил сбой, когда я программно запускал смену страницы, и кто-то прервал ее смахиванием, поэтому мне пришлось временно отключать смахивание каждый раз, когда изменение страницы запускалось в коде ...
— Куба Судер

Ответы:


263

Задокументированный способ предотвращения UIPageViewControllerпрокрутки - не назначать dataSourceсвойство. Если вы назначите источник данных, он перейдет в режим навигации на основе жестов, что вы пытаетесь предотвратить.

Без источника данных вы вручную предоставляете контроллеры представления, когда хотите, с помощью setViewControllers:direction:animated:completionметода, и он будет перемещаться между контроллерами представления по запросу.

Вышеизложенное можно вывести из документации Apple по UIPageViewController (Обзор, второй абзац):

Для поддержки навигации на основе жестов необходимо предоставить контроллеры представления с помощью объекта источника данных.


1
Я тоже предпочитаю это решение. Вы можете сохранить старый источник данных в переменной, установить для источника данных значение nil, а затем восстановить старый источник данных, чтобы снова включить прокрутку. Не все так чисто, но гораздо лучше, чем обход базовой иерархии представлений в поисках прокрутки.
— Matt R

3
Отключение источника данных вызывает проблемы, когда это делается в ответ на уведомления клавиатуры для вложенных представлений текстовых полей. Итерация по вложенным представлениям представления для поиска UIScrollView кажется более надежной.
— AR Younce 01

9
Разве это не убирает точки внизу экрана? И если у вас нет прокрутки и точек, нет особого смысла в использовании PageViewController вообще. Я предполагаю, что спрашивающий хочет сохранить точки.
— Лео Флаэрти,

Это помогло решить проблему, с которой я столкнулся, когда страницы загружаются асинхронно и пользователь делает жест смахивания. Браво :)
— Ja͢ck

1
А. Р. Юнс прав. Если вы отключаете источник данных PageViewController в ответ на уведомление с клавиатуры (например, вы не хотите, чтобы пользователь выполнял горизонтальную прокрутку, когда клавиатура поднята), ваша клавиатура немедленно исчезнет, ​​когда вы обнуляете источник данных PageViewController.
— micnguyen

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

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

        view.scrollEnabled = NO;
    }
}

5
Этот сохраняет контроль над страницей, что приятно.
— Маркус Адамс

1
Это отличный подход, поскольку он сохраняет контроль страницы (маленькие точки). Вы захотите обновить их при изменении страницы вручную. Я использовал этот ответ для этого stackoverflow.com/a/28356383/1071899
— Simon Bøgh

1
Почему бы и нетfor (UIView *view in self.pageViewController.view.subviews) {
— dengApro

Обратите внимание, что пользователи по-прежнему смогут перемещаться между страницами, касаясь элемента управления страницей в левой и правой половине.
— 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) как расширения:

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 }
— Wagner Sales

Получатель здесь возвращает только включенный статус последнего подпредставления.
— Эндрю Дункан

8

Я уже некоторое время борюсь с этим и подумал, что должен опубликовать свое решение, следуя ответу Джесседа; удаление источника данных 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

Изменить : этот ответ работает только для стиля загиба страницы. Ответ Джесседа намного лучше: работает независимо от стиля и полагается на задокументированное поведение .

UIPageViewController предоставляет свой массив распознавателей жестов, которые вы можете использовать для их отключения:

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

8
Вы тестировали свой код? myPageViewController.gestureRecognizers всегда пусто
— user2159978 03

11
похоже, ваше решение работает для стиля
— загиба

8
Не работает горизонтальная прокрутка. Возвращаемое значение всегда является пустым массивом.
— Khanh Nguyen

вы, ребята, поняли это для стиля прокрутки?
— Esqarrouth

4

Если вы хотите, чтобы у вас UIPageViewControllerсохранялась возможность пролистывания, при этом позволяя элементам управления контентом использовать свои функции (проведите пальцем для удаления и т. Д.), Просто выключите canCancelContentTouchesв UIPageViewController.

Поместите это в вашей UIPageViewController«S viewDidLoadFUNC. (Swift)

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

У UIPageViewControllerнего есть автоматически сгенерированное подвид, которое обрабатывает жесты. Мы можем запретить этим подпредставлениям отменять жесты содержимого.

Из...

Проведите пальцем по экрану, чтобы удалить tableView, который находится внутри pageViewController


Спасибо, Картер. Я пробовал твое решение. Однако бывают случаи, когда pageViewController перехватывает действие горизонтального смахивания в сторону от представления дочерней таблицы. Как я могу гарантировать, что когда пользователь проводит пальцем по дочернему табличному представлению, табличному представлению всегда уделяется приоритетное внимание, чтобы получить жест первым?
— Дэвид Лю

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

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

Благодаря ответу @ 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 должен реализовывать вышеуказанный протокол.

Шаг 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

Существует гораздо более простой подход, чем предлагают здесь большинство ответов, а именно возврат nilв обратных вызовах viewControllerBeforeи viewControllerAfterdataSource.

Это отключает жест прокрутки на устройствах iOS 11+, сохраняя при этом возможность использовать dataSource(для таких вещей, как presentationIndex/ 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) Вы можете удалить gestureRecognizers вашего pageViewController:

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

Перечисление подпредставлений для поиска scrollView UIPageViewControllerне сработало для меня, так как я не могу найти 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
        }
    }
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.