Як відключити жест пальцем UIPageViewController?


125

У моєму випадку батько UIViewControllerмістить те, UIPageViewControllerщо містить, UINavigationControllerщо міститьUIViewController . Мені потрібно додати жест пальцем до останнього контролера перегляду, але пальцями обробляються так, ніби вони належать до контролера перегляду сторінки. Я намагався це робити як програмно, так і через xib, але безрезультатно.

Тож, як я розумію, я не можу досягти своєї мети, поки не UIPageViewControllerвпорається з її жестами. Як вирішити це питання?


7
ВикористанняpageViewControllerObject.view.userInteractionEnabled = NO;
Шрікант

6
неправильно, оскільки він також блокує весь його вміст. Але мені потрібно блокувати лише його жести
user2159978

Чому ви це робите? Якої поведінки ви бажаєте з програми? Це здається, що ви надмірно ускладнюєте архітектуру. Чи можете ви пояснити, до якої поведінки ви прагнете.
Фогмайстер

Одна зі сторінок у програмі UIPageViewControllerмає UINavigationController. Клієнт хоче
натиснути

@Srikanth дякую, саме це мені було потрібно! Перегляд моєї сторінки припинився, коли я запрограмував зміну сторінки програмно, і хтось перебив її перенесенням, тому мені довелося відключити перегляд часу тимчасово, коли зміна сторінки запускається в коді ...
Kuba Suder

Відповіді:


262

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

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

Сказане може бути виведено з документації Apple на UIPageViewController (Огляд, другий абзац):

Щоб підтримувати навігацію на основі жестів, ви повинні надати контролерам перегляду, використовуючи об’єкт джерела даних.


1
Я також вважаю за краще це рішення. Ви можете зберегти старий джерело даних у змінній, встановити джерело даних на нуль, а потім відновити старий джерело даних, щоб повторно включити прокрутку. Не все так чисто, але набагато краще, ніж обхід основної ієрархії подання, щоб знайти подання прокрутки.
Метт Р

3
Вимкнення джерела даних спричиняє проблеми під час виконання у відповідь на сповіщення клавіатури для підпрезентацій текстового поля. Ітерація над підглядами подання для пошуку UIScrollView видається більш надійною.
AR Younce

9
Це не видаляє точки внизу екрана? І якщо у вас немає ні пальців, ні точок, взагалі не так багато сенсу використовувати PageViewController. Я здогадуюсь, що запитувач хоче зберегти крапки.
Лев Флахерті

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

1
A. R Отже, це правильно. Якщо ви вимкнете джерело даних 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
Саймон 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's) як розширення:

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 }
Продаж Вагнера

Тут геть повертає лише увімкнений стан останнього підпідгляду.
Ендрю Дункан

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

Я сподіваюся, що це допомагає комусь, його не так чисто, як мені б хотілося, але він не відчуває себе занадто хитким і т.д.

EDIT: Якщо хтось хоче перевести це на Objective-C, будь ласка, зробіть :)


8

Редагувати : ця відповідь працює лише для стилю скручування сторінки. Відповідь Джесседа набагато краща: працює незалежно від стилю та покладається на документально підтверджену поведінку .

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

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

8
Ви перевірили свій код? myPageViewController.gestureRecognizers завжди порожній
user2159978

11
здається, що ваше рішення працює в стилі завитка сторінок, але в моєму додатку я використовую стиль перегляду прокрутки
user2159978

8
Не працює для горизонтальної прокрутки. Повернене значення - це завжди порожній масив.
Хан Нгуен

Ви, хлопці, зрозуміли це за стилем прокрутки?
Esqarrouth

4

Якщо ви хочете, щоб ваша UIPageViewControllerфункція зберігала здатність переносити пальцем, дозволяючи контролерам вмісту використовувати їх функції (проведіть пальцем для видалення тощо), просто вимкніть canCancelContentTouchesвUIPageViewController .

Покладіть це у своє UIPageViewController «S viewDidLoadFUNC. (Швидкий)

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

The UIPageViewControllerМає автоматично згенерований підвид , який обробляє жести. Ми можемо не допустити, щоб ці підзагляди скасовували жести вмісту.

З ...

Проведіть пальцем, щоб видалити на tableView, який знаходиться всередині сторінкиViewController


Дякую Картеру. Я спробував ваше рішення. Однак, все ще існують випадки, коли 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

Швидкий 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:

Покладіть на кожен з елементів ПВХ відповідальність за те, чи ввімкнено прокрутку ліворуч і праворуч.

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

Кожен з ваших контролерів перегляду ПВХ повинен реалізувати вищезазначений протокол.

Крок 2:

У ваших контролерах ПВХ відключіть прокрутку, якщо потрібно:

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

class SomeViewController: UIViewController {
    // ...    
}

Крок 3:

Додайте ефективні методи блокування прокрутки до свого ПВХ:

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

Переклад @ @ користувача2159978 відповіді на C #:

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

-1

(Swift 4) Ви можете видалити жестиРознавці своєї сторінкиViewController:

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
        }

    }
}

Додайте це до класу перегляду контролерів сторінки. 100% працює


прокоментуйте, будь ласка, з якою проблемою ви
стикаєтесь

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