Gesture Recognizer — co to, obsługa dotyku w iOS

Autor: IT Sectr Opublikowano: 2026-02-27 Czas czytania: 8 min
Gesture Recognizer — abstrakcja w UIKit, która przekształca sekwencję dotknięć użytkownika w rozpoznany gest: tap, przesunięcie, długie naciśnięcie, ściśnięcie lub obrót. Zamiast ręcznego śledzenia współrzędnych w metodach touchesBegan/touchesMoved, programista dodaje gotowy obiekt UIGestureRecognizer do UIView i implementuje metodę akcji. Według Apple Developer Documentation, UIGestureRecognizer obsługuje 7 wbudowanych podklas, pokrywających 95% typowych gestów w aplikacjach mobilnych.

Najważniejsze

  • UIGestureRecognizer — abstrakcyjna klasa UIKit do rozpoznawania wzorców dotknięć: tapów, przesunięć, ściśnięć, obrotów i długich naciśnięć.
  • 7 wbudowanych podklas: UITapGestureRecognizer, UISwipeGestureRecognizer, UIPinchGestureRecognizer, UIRotationGestureRecognizer, UILongPressGestureRecognizer, UIPanGestureRecognizer, UIScreenEdgePanGestureRecognizer.
  • Każdy rozpoznawacz ma stan (possible, began, changed, ended, cancelled, failed), który aktualizuje się w miarę ruchu palców.
  • Do rozwiązywania konfliktów między gestami używa się metody require(toFail:), która określa priorytet rozpoznawania.
  • Gesture Recognizer działa z UIKit i UIViewRepresentable w SwiftUI, ale w natywnym SwiftUI stosuje się modyfikatory gestów.

Co to jest Gesture Recognizer?

UIGestureRecognizer — abstrakcyjna klasa bazowa w UIKit, dekomponująca proces rozpoznawania gestów na osobne obiekty. Każda podklasa odpowiada za jeden typ gestu: UITapGestureRecognizer — za tap określonej liczby palców, UISwipeGestureRecognizer — za przesunięcie w zadanym kierunku. Programista dodaje rozpoznawacz do UIView przez metodę addGestureRecognizer(:), a UIKit automatycznie śledzi dotknięcia, aktualizuje stan i wywołuje akcję po rozpoznaniu.

Przed pojawieniem się UIGestureRecognizer (iOS 3.2, 2010) programiści nadpisywali metody UIResponder — touchesBegan, touchesMoved, touchesEnded — i ręcznie analizowali trajektorię dotknięć. Prowadziło to do duplikacji kodu i błędów przy obsłudze jednoczesnych dotknięć. Apple enkapsulowało tę logikę w UIGestureRecognizer, dodając obsługę multitouch, anulowania gestów i jednoczesnej pracy wielu rozpoznawaczy na jednym widoku.

Według sesji WWDC, Gesture Recognizer obsługuje do 11 jednoczesnych dotknięć na iPadzie i 5 na iPhonie. W IT Sectr używamy UIGestureRecognizer jako standardowego sposobu obsługi wprowadzania danych przez użytkownika we wszystkich projektach UIKit — wyeliminowało to błędy związane z ręcznym śledzeniem touchesBegan.

Stany UIGestureRecognizer

Każdy UIGestureRecognizer przechodzi przez 7 możliwych stanów, zdefiniowanych w enum UIGestureRecognizer.State. Te stany odzwierciedlają cykl życia rozpoznawania: od określenia dotknięcia do zakończenia lub anulowania gestu. Zrozumienie stanów jest kluczowe dla implementacji niestandardowych rozpoznawaczy i debugowania konfliktów.

Stan Znaczenie Kiedy występuje
.possible Stan początkowy, gest jeszcze nierozpoznany Natychmiast po dodaniu do widoku
.began Gest rozpoznany i rozpoczęty Przy pierwszym ruchu palca dla pan/longPress
.changed Parametry gestu uległy zmianie (współrzędne, kąt) Przy każdym ruchu palca
.ended Użytkownik zdjął palec, gest zakończony Przy touchesEnded
.cancelled Gest przerwany przez system (połączenie przychodzące, zmiana orientacji) Przy touchesCancelled
.failed Gest nierozpoznany zgodnie z warunkami Przy touchesCancelled bez rozpoznania
.recognized Synonim .ended; gest pomyślnie rozpoznany To samo co .ended

Gesty dyskretne (tap, swipe) przechodzą z .possible bezpośrednio do .ended lub .failed. Gesty ciągłe (pan, pinch, rotation, longPress) przechodzą .possible → .began → .changed (wielokrotnie) → .ended. W metodzie akcji sprawdzaj gestureRecognizer.state — to pozwala odróżnić początek, zmianę i koniec gestu.

Rodzaje gestów w iOS

UIKit udostępnia 7 wbudowanych podklas UIGestureRecognizer, pokrywających większość scenariuszy interakcji. Każda podklasa ma specyficzne ustawienia: numberOfTapsRequired dla tapu, direction dla przesunięcia, minimumPressDuration dla długiego naciśnięcia.

  • UITapGestureRecognizer — rozpoznaje pojedyncze i wielokrotne tapy. Konfiguruje się liczbę dotknięć (numberOfTapsRequired) i palców (numberOfTouchesRequired). Używany do przycisków, linków, wyboru elementów.
  • UISwipeGestureRecognizer — rozpoznaje przesunięcie w jednym z czterech kierunków (right, left, up, down). Właściwość direction określa kierunek; numberOfTouchesRequired — liczbę palców.
  • UIPanGestureRecognizer — gest ciągły do przeciągania. Metoda translation(in:) zwraca przesunięcie od punktu początkowego. Używany do drag-and-drop, karuzel, pull-to-refresh.
  • UIPinchGestureRecognizer — skalowanie dwoma palcami. Właściwość scale odzwierciedla bieżący współczynnik skali. Szczegółowo omówiony w artykule Pinch-to-Zoom.
  • UIRotationGestureRecognizer — obrót dwoma palcami. Właściwość rotation przechowuje kąt w radianach. Używany do obracania obrazów, map, kanw.
  • UILongPressGestureRecognizer — długie naciśnięcie. Parametry minimumPressDuration (sekundy), allowableMovement (piksele). Używany do menu kontekstowych, przeciągania elementów.
  • UIScreenEdgePanGestureRecognizer — panoramowanie od krawędzi ekranu. Właściwość edges określa, którą krawędź śledzić. Używany do gestów nawigacyjnych, zasłon powiadomień.

Do niestandardowych gestów (np. rysowanie zygzaka) tworzy się podklasę UIGestureRecognizer z nadpisaniem metod touchesBegan, touchesMoved, touchesEnded i aktualizacją stanu. Apple zaleca używanie wbudowanych klas, gdzie to możliwe — są zoptymalizowane i poprawnie współdziałają ze sobą.

Rozwiązywanie konfliktów między gestami

Gdy na jednym UIView używanych jest kilka UIGestureRecognizer (np. tap i double-tap), powstaje konflikt rozpoznawania: przy podwójnym tapie najpierw uruchamia się pojedynczy tap. Do rozwiązania Apple udostępnia metodę require(toFail:), która opóźnia rozpoznanie jednego gestu, dopóki drugi nie zakończy się niepowodzeniem.

Mechanizm działa następująco: wywołując tapRecognizer.require(toFail: doubleTapRecognizer), określasz, że tapRecogniser przejdzie do stanu .recognized dopiero po tym, jak doubleTapRecogniser zakończy się w .failed. To dodaje opóźnienie ~0.3 sekundy przed wykonaniem pojedynczego tapu — użytkownik naciska dwukrotnie, a pierwszy tap jest ignorowany. Alternatywną metodą jest delegat UIGestureRecognizerDelegate z metodą gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:), która zezwala na jednoczesne rozpoznawanie (np. pan + pinch dla mapy).

Według WWDC 2020, około 15% błędów w aplikacjach UIKit jest związanych z nieprawidłową konfiguracją konfliktów gestów. W IT Sectr standaryzowaliśmy podejście: dla każdego ekranu tworzony jest schemat gestów z określonymi priorytetami require(toFail:) — to całkowicie wyeliminowało błędy z podwójnym uruchamianiem tapów.

Przykłady kodu w Swift

Przykład 1: UITapGestureRecognizer — tap na obrazie

Dodaje obsługę pojedynczego tapu na UIImageView. Po tapnięciu obraz zmienia przezroczystość — najprostszy przykład pokazujący przypisanie gestu do widoku.

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

Kluczowy moment: isUserInteractionEnabled na UIImageView domyślnie ma wartość false — bez tej flagi Gesture Recognizer nie będzie otrzymywać dotknięć. Dla UIView i UIButton flaga jest domyślnie włączona. Metoda akcji przyjmuje parametr UITapGestureRecognizer, przez który można uzyskać location(in:) do określenia współrzędnych tapu.

Przykład 2: UISwipeGestureRecognizer — przesunięcie do nawigacji

Implementuje przesunięcie w lewo, aby powrócić do poprzedniego ekranu. Demonstruje konfigurację kierunku i przypisanie gestu do widoku głównego kontrolera.

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 — gest dyskretny: przechodzi do .recognised natychmiast po rozpoznaniu, bez pośrednich .changed. Dlatego w akcji nie trzeba sprawdzać stanu — gest albo jest rozpoznany (wywołanie akcji), albo nie. Właściwość direction przyjmuje jedną z czterech wartości: .left, .right, .up, .down. Aby obsługiwać wiele kierunków, utwórz osobne rozpoznawacze dla każdego.

Przykład 3: Kombinacja gestów z require(toFail:)

Pokazuje, jak skonfigurować pojedynczy i podwójny tap na jednym widoku bez konfliktu. Rozpoznawacz podwójnego tapu ma priorytet — pojedynczy tap uruchamia się tylko wtedy, gdy podwójny nie został rozpoznany.

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

Bez require(toFail:) przy podwójnym tapie najpierw wywoła się handleSingleTap, potem handleDoubleTap — to psuje UX. Z require(toFail:) pojedynczy tap czeka ~0.3 sekundy, aby upewnić się, że drugi tap nie nastąpi. W IT Sectr ten wzorzec jest używany w edytorach obrazów i galeriach, gdzie podwójny tap przybliża, a pojedynczy — zaznacza element.

Często zadawane pytania

Czy można używać wielu Gesture Recognizer na jednym widoku?

Tak, UIView obsługuje wiele UIGestureRecognizer jednocześnie. Do rozwiązywania konfliktów użyj metody require(toFail:), która określa kolejność rozpoznawania. Do równoległej pracy gestów (np. pan + pinch na mapie) zaimplementuj delegata gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:), zwracającego true.

Czym UIGestureRecognizer różni się od touchesBegan?

UIGestureRecognizer — wysokopoziomowa abstrakcja, która automatycznie rozpoznaje wzorce dotknięć i zarządza stanami. touchesBegan — niskopoziomowa metoda UIResponder, wymagająca ręcznego śledzenia współrzędnych, czasów i anulowania dotknięć. Gesture Recognizer jest prostszy, bardziej niezawodny i preferowany dla standardowych gestów; touchesBegan jest uzasadniony tylko dla niestandardowej grafiki.

Czy Gesture Recognizer działa w SwiftUI?

W natywnym SwiftUI używa się modyfikatorów: onTapGesture, onLongPressGesture, DragGesture, MagnificationGesture, RotationGesture. Są to deklaratywne odpowiedniki UIGestureRecognizer, zintegrowane z hierarchią SwiftUI. W razie potrzeby można opakować UIKit-rozpoznawacz przez UIViewRepresentable, ale Apple zaleca używanie natywnych gestów SwiftUI.

Podsumowanie

  • UIGestureRecognizer — abstrakcja UIKit do dekompozycji logiki rozpoznawania gestów na osobne obiekty z automatycznym zarządzaniem stanami.
  • 7 wbudowanych podklas pokrywa 95% typowych gestów: tap, przesunięcie, panoramowanie, ściśnięcie, obrót, długie naciśnięcie i przesunięcie od krawędzi.
  • Każdy rozpoznawacz przechodzi 7 stanów — od .possible do .ended/.failed, co pozwala precyzyjnie śledzić gesty dyskretne i ciągłe.
  • Metoda require(toFail:) rozwiązuje konflikty między gestami na jednym widoku, określając priorytet rozpoznawania.
  • W SwiftUI używa się deklaratywnych modyfikatorów gestów, ale UIKit-rozpoznawacze są dostępne przez UIViewRepresentable.
  • Gesture Recognizer jest obowiązkowy dla projektów UIKit; touchesBegan jest uzasadniony tylko dla niestandardowego rysowania i niskopoziomowej grafiki.
  • Używaj require(toFail:) dla par tap/double-tap i gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:) dla równoległych gestów.

Opracujemy aplikację mobilną pod klucz

IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.

Omów projekt

Przeczytaj również