Конфликт между жестом панорамирования ScrollView и panGestureRecognizer

У меня есть UIScrollView в UIViewController, который модально отображается переходом, а еще UIPanGestureRecognizer закрывает контроллер представления панорамированием. Этот жест работает, только если

 scrollView.contentOffset.y == 0

Проблема в том, что теперь два жеста панорамирования конфликтуют друг с другом, и я больше не могу прокручивать представление.

Чтобы решить эту проблему, я попытался использовать метод gestureRecognizer(_: shouldRecognizeSimultaneouslyWith:), вернув да, а также попытался добавить свой собственный жест панорамирования в UIScrollView распознаватель жестов панорамирования следующим образом:

 scrollView.panGestureRecognizer.addTarget(self, action: #selector(handlePanGesture(_:)))

Но это не решает проблему. Если вы знаете, как решить эту проблему, я был бы признателен за вашу помощь.

ИЗМЕНЕНО

Вот код моего жеста панорамирования, который закрывает контроллер представления:

     @IBAction func handlePanGesture(_ sender: UIPanGestureRecognizer) {
    let percentThreshold: CGFloat = 0.3

    if scrollView.contentOffset.y == 0 {
        let translation = sender.translation(in: view)
        let verticalMovement = translation.y / view.bounds.height
        let downwardMovement = fmaxf(Float(verticalMovement), 0.0)
        let downwardMovementPercent = fminf(downwardMovement, 1.0)
        let progress = CGFloat(downwardMovementPercent)

        guard let interactor = interactor else {return}
        switch sender.state {
        case .began:
            interactor.hasStarted = true
            dismiss(animated: true, completion: nil)
        case .changed:
            interactor.shouldFinish = progress > percentThreshold
            interactor.update(progress)
        case .cancelled:
            interactor.hasStarted = false
            interactor.cancel()
        case .ended:
            interactor.hasStarted = false
            interactor.shouldFinish ? interactor.finish() : interactor.cancel()
        default:
            break
        }

    }
}

EDITED_2 Вот код для Interactor:

class Interactor: UIPercentDrivenInteractiveTransition {
var hasStarted = false
var shouldFinish = false 

}

P.s. Я знаю, что есть масса похожих вопросов, но они мне не подходят.


person Tigran Iskandaryan    schedule 28.01.2018    source источник
comment
Если вы используете UINavigationController, он должен автоматически смахнуть, чтобы вернуться, если вы явно не удалите распознаватель жестов. developer.apple.com/documentation/uikit/uinavigationcontroller/   -  person Samah    schedule 29.01.2018
comment
@Samah, проблема не в том, чтобы вернуться. Контроллер моего представления отображается модально, поэтому он не может вернуться назад, проведя пальцем по экрану (даже если он находится в контроллере навигации)   -  person Tigran Iskandaryan    schedule 29.01.2018
comment
Я не понимаю вашего варианта использования. В каком направлении вы ожидаете, что пользователь проведет пальцем, чтобы закрыть диалоговое окно?   -  person Samah    schedule 29.01.2018
comment
@Samah, мой контроллер представления отображается модально, поэтому он отображается в нижней части экрана. Я отклоняю это, панорамируя сверху вниз. Проблема в том, что мой вид прокрутки также прокручивается по вертикали, поэтому существует конфликт между двумя жестами панорамирования, и мне нужно его разрешить.   -  person Tigran Iskandaryan    schedule 29.01.2018
comment
Предполагая, что вам удалось разделить распознаватели панелей, как вы собираетесь определить, какое действие пытался выполнить пользователь?   -  person Samah    schedule 29.01.2018
comment
@Samah, как я уже сказал в своем вопросе, если смещение прокрутки y равно 0, контроллер представления отклоняется (когда пользователь перемещается сверху вниз), потому что в этом случае для прокрутки представления пользователь должен двигать пальцем к вершине   -  person Tigran Iskandaryan    schedule 29.01.2018
comment
Вы когда-нибудь задумывались об использовании только распознавателя прокрутки для обоих действий?   -  person iWheelBuy    schedule 08.03.2018
comment
@iWheelBuy, да, я сделал именно это, используя значение contentOffset.y. Спасибо за предложение!   -  person Tigran Iskandaryan    schedule 08.03.2018
comment
@TigranIskandaryan Рад, что помог! Пожалуйста, напишите свое решение в качестве ответа на ваш вопрос.   -  person iWheelBuy    schedule 09.03.2018


Ответы (4)


Чтобы разрешить прокрутку, когда UIPanGestureRecognizer находится в ScrollView, вам необходимо создать UIGestureRecognizerDelegate, который возвращает true на gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:)

Если вы этого не сделаете, прокрутка в ScrollView будет невозможна.

Делается это так:

let scrollViewPanGesture = UIPanGestureRecognizer(target: self, action: #selector(onPan(_:)))
scrollViewPanGesture.delegate = self
scrollView.addGestureRecognizer(scrollViewPanGesture)

extension ViewController: UIGestureRecognizerDelegate {
    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        return true
    }
}
person bnussey    schedule 08.03.2018
comment
Я пробовал это, но безуспешно. Я решил проблему, просто используя значение scrollView contentOffset.y для отклонения представления. - person Tigran Iskandaryan; 08.03.2018

Я не уверен, но вы можете попробовать добавить ViewController в качестве UIPanGestureRecognizer делегата смахивания, чтобы отклонить жест панорамирования и реализовать gestureRecognizerShouldBegin(_:);

func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
    return scrollView.contentOffset.y == 0
}

Таким образом, жест для отклонения начнется только в том случае, если смещение содержимого равно нулю.

person Rico Crescenzio    schedule 29.01.2018
comment
Спасибо за Ваш ответ. Работает, но только перед скроллингом (я могу как убрать ВК, так и прокрутить). Но после некоторой прокрутки (и возврата в положение, где scrollView.contentOffset.y == 0) мой жест панорамирования перестает работать. Может быть, вы знаете, как с этим справиться? - person Tigran Iskandaryan; 29.01.2018
comment
Вы можете поделиться другим кодом? Я не знаю, правильно ли понимаю вашу проблему - person Rico Crescenzio; 30.01.2018
comment
Я не знаю, что именно вам нужно, поэтому я добавил код для своего жеста панорамирования. - person Tigran Iskandaryan; 30.01.2018
comment
Я также заметил, что когда я прокручиваю область прокрутки до ее вершины, смещение содержимого не совсем равно 0,0. Но даже если я программно установил его на 0,0 и сработает точка останова внутри метода, мой пользовательский жест панорамирования перестанет работать (работает только панорама прокрутки) - person Tigran Iskandaryan; 30.01.2018
comment
Что такое интерактор? - person Rico Crescenzio; 30.01.2018
comment
Interactor - это просто UIPercentDrivenInteractiveTransition подкласс. Я тоже добавил его код - person Tigran Iskandaryan; 30.01.2018
comment
Вы вернете истину в gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:)? - person Rico Crescenzio; 31.01.2018
comment
Да, я просто возвращаю true из этого метода (нет разницы в поведении, если я верну false) - person Tigran Iskandaryan; 31.01.2018
comment
Я могу взглянуть на ваш исходный код, если хотите; на самом деле я не очень хорошо понимаю макет и точное поведение, которого вы хотите достичь - person Rico Crescenzio; 31.01.2018
comment
Вот чего я хочу добиться: вы знаете, как контроллер представления выглядит модально (его анимация). Теперь я хочу отменить его анимацию с помощью жеста панорамирования (в основном, закрыть контроллер представления с помощью жеста панорамирования, чтобы он исчез снизу). Извините, но я не могу сейчас поделиться всем исходным кодом (у меня просто нет на это прав). Ничего личного. Однако при желании могу поделиться кодом аниматора целиком. - person Tigran Iskandaryan; 31.01.2018

Добавьте subview под scrollview и добавьте к нему жест панорамирования вместо того, чтобы добавлять его в self.view, который наверняка будет конфликтовать с тем, что у scrollview

person Sh_Khan    schedule 28.01.2018
comment
Я добавил, но теперь мое действие с настраиваемым жестом панорамирования не вызывается. Я предполагаю, что причина в том, что мой вид прокрутки закрывает вид под ним, поэтому он не получает вызова - person Tigran Iskandaryan; 29.01.2018
comment
Переопределить HitTest: withEvent: представления прокрутки: вернуть подпредставление, если точка касания находится в кадре подпредставления - person rpstw; 29.01.2018

Вы поступили правильно, когда реализовали gestureRecognizer(_: shouldRecognizeSimultaneouslyWith:)
Но сначала вы должны установить делегата жеста на текущий контроллер представления:

let panGesture = UIPanGestureRecognizer.init(target: self, action: #selector(handlePanGesture(_:)))
panGesture.delegate = self // <--THIS
scrollView.addGestureRecognizer(panGesture)
person ngbaanh    schedule 29.01.2018
comment
Я сделал это, но, к сожалению, безуспешно. Моя кастомная панорама съедает этот жест, поэтому жест прокрутки не вызывается. Итак, проблема не в том, что я не могу вызвать свой жест панорамирования, а в том, что я не могу вызвать жест панорамирования в режиме прокрутки для прокрутки. - person Tigran Iskandaryan; 29.01.2018