Как управлять несколькими UIGestureRecognizer на одном UIView, чтобы избежать конфликтов?

«Как управлять несколькими UIGestureRecognizer на одном UIView, чтобы избежать конфликтов?» — вопрос из категории UIKit, который задают на 10% собеседований IOS Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Используйте протокол UIGestureRecognizerDelegate для тонкого управления распознаванием жестов.

Основные методы делегата:

  1. Разрешение одновременной работы:

    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer,
                          shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        // Пример: разрешаем одновременное распознавание pinch и rotate
        return (gestureRecognizer is UIPinchGestureRecognizer && 
                otherGestureRecognizer is UIRotationGestureRecognizer)
    }
  2. Установка зависимости: Жест gestureA будет распознан, только если жест gestureB не сработал.

    // Долгое нажатие должно провалиться, чтобы сработал обычный тап
    singleTapGesture.require(toFail: longPressGesture)
  3. Условное распознавание: Можно запретить распознавание жеста в определенных условиях.

    func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
        // Запрещаем свайп, если, например, включен режим редактирования
        return !isEditingModeEnabled
    }

Практический пример: На UIImageView нужно обрабатывать и тап (для выбора), и пан (для перемещения).

let tap = UITapGestureRecognizer(target: self, action: #selector(handleTap))
let pan = UIPanGestureRecognizer(target: self, action: #selector(handlePan))
pan.delegate = self
imageView.addGestureRecognizer(tap)
imageView.addGestureRecognizer(pan)

// В расширении, реализующем UIGestureRecognizerDelegate:
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer,
                      shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
    // Разрешаем тапу и панам работать вместе
    return true
}