Gesture Recognizer — какво е, обработка на докосвания в iOS

Автор: IT Sectr Публикувано: 2026-02-27 Време за четене: 8 мин
Gesture Recognizer — абстракция в UIKit, която преобразува последователност от докосвания на потребителя в разпознат жест: тап, преместване, дълго натискане, прищипване или завъртане. Вместо ръчно проследяване на координати в методите touchesBegan/touchesMoved, разработчикът добавя готов обект UIGestureRecognizer към UIView и имплементира action метод. Според Apple Developer Documentation, UIGestureRecognizer поддържа 7 вградени подкласа, покриващи 95% от типичните жестове в мобилни приложения.

Основни точки

  • UIGestureRecognizer — абстрактен клас на UIKit за разпознаване на модели на докосване: тапове, премествания, прищипвания, завъртания и дълги натискания.
  • 7 вградени подкласа: UITapGestureRecognizer, UISwipeGestureRecognizer, UIPinchGestureRecognizer, UIRotationGestureRecognizer, UILongPressGestureRecognizer, UIPanGestureRecognizer, UIScreenEdgePanGestureRecognizer.
  • Всеки разпознавач има състояние (possible, began, changed, ended, cancelled, failed), което се актуализира с движението на пръстите.
  • За разрешаване на конфликти между жестове се използва методът require(toFail:), който задава приоритет на разпознаване.
  • Gesture Recognizer работи с UIKit и UIViewRepresentable в SwiftUI, но в родния SwiftUI се прилагат модификатори на жестове.

Какво е Gesture Recognizer?

UIGestureRecognizer — абстрактен базов клас в UIKit, който декомпозира процеса на разпознаване на жестове в отделни обекти. Всеки подклас отговаря за един тип жест: UITapGestureRecognizer — за тап с определен брой пръсти, UISwipeGestureRecognizer — за преместване в зададена посока. Разработчикът добавя разпознавача към UIView чрез метода addGestureRecognizer(:), и UIKit автоматично проследява докосванията, актуализира състоянието и извиква действие при разпознаване.

Преди появата на UIGestureRecognizer (iOS 3.2, 2010) разработчиците презаписваха методите на UIResponder — touchesBegan, touchesMoved, touchesEnded — и ръчно анализираха траекторията на докосванията. Това водеше до дублиране на код и грешки при обработка на едновременни докосвания. Apple капсулира тази логика в UIGestureRecognizer, добавяйки поддръжка за мултитъч, отмяна на жестове и едновременна работа на няколко разпознавача върху един изглед.

Според сесиите на WWDC, Gesture Recognizer обработва до 11 едновременни докосвания на iPad и 5 на iPhone. В IT Sectr използваме UIGestureRecognizer като стандартен начин за обработка на потребителски вход във всички UIKit проекти — това елиминира грешките, свързани с ръчно проследяване на touchesBegan.

Състояния на UIGestureRecognizer

Всеки UIGestureRecognizer преминава през 7 възможни състояния, дефинирани в enum UIGestureRecognizer.State. Тези състояния отразяват жизнения цикъл на разпознаване: от откриване на докосване до завършване или отмяна на жеста. Разбирането на състоянията е критично за имплементиране на персонализирани разпознавачи и отстраняване на конфликти.

Състояние Значение Кога възниква
.possible Начално състояние, жестът все още не е разпознат Веднага след добавяне към изглед
.began Жестът е разпознат и е започнал да се изпълнява При първото движение на пръста за pan/longPress
.changed Параметрите на жеста са се променили (координати, ъгъл) При всяко движение на пръста
.ended Потребителят е вдигнал пръста, жестът е завършен При touchesEnded
.cancelled Жестът е прекъснат от системата (входящо повикване, промяна на ориентация) При touchesCancelled
.failed Жестът не е разпознат според условията При touchesCancelled без разпознаване
.recognized Синоним на .ended; жестът е успешно разпознат Същото като .ended

Дискретните жестове (tap, swipe) преминават от .possible директно в .ended или .failed. Непрекъснатите жестове (pan, pinch, rotation, longPress) преминават през .possible → .began → .changed (многократно) → .ended. В action метода проверявайте gestureRecognizer.state — това позволява различаване на началото, промяната и края на жеста.

Видове жестове в iOS

UIKit предоставя 7 вградени подкласа на UIGestureRecognizer, покриващи повечето сценарии на взаимодействие. Всеки подклас има специфични настройки: numberOfTapsRequired за тап, direction за преместване, minimumPressDuration за дълго натискане.

  • UITapGestureRecognizer — разпознава единични и множествени тапове. Настройва се брой докосвания (numberOfTapsRequired) и пръсти (numberOfTouchesRequired). Използва се за бутони, връзки, избор на елементи.
  • UISwipeGestureRecognizer — разпознава преместване в една от четирите посоки (right, left, up, down). Свойството direction задава посоката; numberOfTouchesRequired — броя на пръстите.
  • UIPanGestureRecognizer — непрекъснат жест за влачене. Методът translation(in:) връща изместването от началната точка. Използва се за drag-and-drop, въртележки, pull-to-refresh.
  • UIPinchGestureRecognizer — мащабиране с два пръста. Свойството scale отразява текущия коефициент на мащаб. Подробно разгледан в статията Pinch-to-Zoom.
  • UIRotationGestureRecognizer — завъртане с два пръста. Свойството rotation съхранява ъгъла в радиани. Използва се за завъртане на изображения, карти, платна.
  • UILongPressGestureRecognizer — дълго натискане. Параметри minimumPressDuration (секунди), allowableMovement (пиксели). Използва се за контекстни менюта, влачене на елементи.
  • UIScreenEdgePanGestureRecognizer — панорамиране от ръба на екрана. Свойството edges определя кой ръб да се проследява. Използва се за навигационни жестове, завеси за известия.

За персонализирани жестове (например рисуване на зигзаг) се създава подклас на UIGestureRecognizer с презаписване на методите touchesBegan, touchesMoved, touchesEnded и актуализиране на състоянието. Apple препоръчва използването на вградени класове, където е възможно — те са оптимизирани и взаимодействат правилно помежду си.

Разрешаване на конфликти между жестове

Когато на един UIView се използват няколко UIGestureRecognizer (например tap и double-tap), възниква конфликт на разпознаване: при двоен тап първо се задейства единичният тап. За решение Apple предоставя метода require(toFail:), който забавя разпознаването на един жест, докато друг не приключи с неуспех.

Механизмът работи по следния начин: извиквайки tapRecognizer.require(toFail: doubleTapRecognizer), вие указвате, че tapRecogniser ще премине в състояние .recognized едва след като doubleTapRecogniser завърши в .failed. Това добавя закъснение от ~0.3 секунди преди изпълнението на единичния тап — потребителят натиска два пъти, а първият тап се игнорира. Алтернативен метод — делегатът UIGestureRecognizerDelegate с метода gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:), който позволява едновременно разпознаване (например pan + pinch за карта).

Според WWDC 2020, около 15% от грешките в UIKit приложенията са свързани с неправилна конфигурация на конфликти на жестове. В IT Sectr стандартизирахме подхода: за всеки екран се създава схема на жестове с приоритети require(toFail:) — това напълно елиминира грешките с двойно задействане на тапове.

Примери за код в Swift

Пример 1: UITapGestureRecognizer — тап върху изображение

Добавя обработчик на единичен тап към UIImageView. При тап изображението променя прозрачността — най-простият пример, показващ свързване на жест с изглед.

swift
import UIKit

class ImageViewController: UIViewController {

    @IBOutlet private var imageView: UIImageView!

    override func viewDidLoad() {
        super.viewDidLoad()
        let tap = UITapGestureRecognizer(
            target: self,
            action: #selector(handleTap(_:))
        )
        tap.numberOfTapsRequired = 1
        imageView.addGestureRecognizer(tap)
        imageView.isUserInteractionEnabled = true
    }

    @objc private func handleTap(_: UITapGestureRecognizer) {
        UIView.animate(withDuration: 0.2) {
            self.imageView.alpha = self.imageView.alpha == 1.0 ? 0.5 : 1.0
        }
    }
}

Ключов момент: isUserInteractionEnabled на UIImageView по подразбиране е false — без този флаг Gesture Recognizer няма да получава докосвания. За UIView и UIButton флагът е включен по подразбиране. Action методът приема параметър UITapGestureRecognizer, чрез който може да се получи location(in:) за определяне на координатите на тапа.

Пример 2: UISwipeGestureRecognizer — преместване за навигация

Имплементира преместване наляво за връщане към предишния екран. Демонстрира настройка на посоката и свързване на жеста с кореновия изглед на контролера.

swift
import UIKit

class DetailViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let swipeLeft = UISwipeGestureRecognizer(
            target: self,
            action: #selector(handleSwipe(_:))
        )
        swipeLeft.direction = .left
        view.addGestureRecognizer(swipeLeft)
    }

    @objc private func handleSwipe(_: UISwipeGestureRecognizer) {
        navigationController?.popViewController(animated: true)
    }
}

UISwipeGestureRecognizer — дискретен жест: преминава в .recognised веднага след разпознаване, без междинен .changed. Следователно в действието не е необходимо да се проверява състоянието — жестът или е разпознат (действието се извиква), или не. Свойството direction приема една от четирите стойности: .left, .right, .up, .down. За поддръжка на множество посоки създайте отделни разпознавачи за всяка посока.

Пример 3: Комбинация от жестове с require(toFail:)

Показва как да конфигурирате единичен и двоен тап на един и същ изглед без конфликт. Разпознавачът на двоен тап има приоритет — единичният тап се задейства само ако двойният тап не е разпознат.

swift
import UIKit

class TapViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let singleTap = UITapGestureRecognizer(
            target: self,
            action: #selector(handleSingleTap)
        )
        singleTap.numberOfTapsRequired = 1

        let doubleTap = UITapGestureRecognizer(
            target: self,
            action: #selector(handleDoubleTap)
        )
        doubleTap.numberOfTapsRequired = 2

        singleTap.require(toFail: doubleTap)

        view.addGestureRecognizer(singleTap)
        view.addGestureRecognizer(doubleTap)
    }

    @objc private func handleSingleTap() {
        print("Single tap — after 0.3s delay")
    }

    @objc private func handleDoubleTap() {
        print("Double tap — instant")
    }
}

Без require(toFail:) при двоен тап първо ще се извика handleSingleTap, след това handleDoubleTap — това разваля потребителското изживяване. С require(toFail:) единичният тап изчаква ~0.3 секунди, за да се увери, че вторият тап няма да последва. В IT Sectr този модел се използва в редактори на изображения и галерии, където двойният тап увеличава, а единичният — избира елемент.

Често задавани въпроси

Могат ли да се използват множество Gesture Recognizer на един изглед?

Да, UIView поддържа множество UIGestureRecognizer едновременно. За разрешаване на конфликти използвайте метода require(toFail:), който задава реда на разпознаване. За паралелна работа на жестове (например pan + pinch върху карта) имплементирайте делегата gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:), връщащ true.

Как UIGestureRecognizer се различава от touchesBegan?

UIGestureRecognizer — абстракция на високо ниво, която автоматично разпознава модели на докосване и управлява състоянията. touchesBegan — метод на ниско ниво на UIResponder, изискващ ръчно проследяване на координати, време и отмяна на докосвания. Gesture Recognizer е по-прост, по-надежден и предпочитан за стандартни жестове; touchesBegan е оправдан само за персонализирана графика.

Работи ли Gesture Recognizer в SwiftUI?

В родния SwiftUI се използват модификатори: onTapGesture, onLongPressGesture, DragGesture, MagnificationGesture, RotationGesture. Това са декларативни аналози на UIGestureRecognizer, интегрирани в йерархията на SwiftUI. При необходимост можете да обвиете UIKit разпознавач чрез UIViewRepresentable, но Apple препоръчва използването на родни SwiftUI жестове.

Резюме

  • UIGestureRecognizer — UIKit абстракция за декомпозиция на логиката за разпознаване на жестове в отделни обекти с автоматично управление на състоянията.
  • 7 вградени подкласа покриват 95% от типичните жестове: тап, преместване, панорамиране, прищипване, завъртане, дълго натискане и преместване от ръба.
  • Всеки разпознавач преминава през 7 състояния — от .possible до .ended/.failed, позволявайки точно проследяване на дискретни и непрекъснати жестове.
  • Методът require(toFail:) разрешава конфликти между жестове на един и същ изглед, задавайки приоритет на разпознаване.
  • В SwiftUI се използват декларативни модификатори на жестове, но UIKit разпознавачите са достъпни чрез UIViewRepresentable.
  • Gesture Recognizer е задължителен за UIKit проекти; touchesBegan е оправдан само за персонализирано рисуване и графика на ниско ниво.
  • Използвайте require(toFail:) за двойки tap/double-tap и gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:) за паралелни жестове.

Ще разработим мобилно приложение под ключ

IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.

Обсъдете проекта

Прочетете също