Főbb pontok
UIGestureRecognizer — egy absztrakt alaposztály a UIKit-ben, amely a gesztusfelismerési folyamatot különálló objektumokra bontja. Minden alosztály egy gesztustípusért felel: UITapGestureRecognizer — meghatározott számú ujjal történő taphoz, UISwipeGestureRecognizer — adott irányú swipchez. A fejlesztő a felismerőt az addGestureRecognizer(:) metódussal adja hozzá a UIView-hez, és az UIKit automatikusan követi az érintéseket, frissíti az állapotot, és felismeréskor meghívja az akciót.
Az UIGestureRecognizer (iOS 3.2, 2010) megjelenése előtt a fejlesztők felülírták a UIResponder metódusait — touchesBegan, touchesMoved, touchesEnded — és manuálisan elemezték az érintések pályáját. Ez kódduplikációhoz és hibákhoz vezetett az egyidejű érintések feldolgozásakor. Az Apple ezt a logikát az UIGestureRecognizer-be kapszulázta, hozzáadva a multitouch, a gesztusok megszakításának és több felismerő egy nézeten történő egyidejű működésének támogatását.
A WWDC ülések szerint a Gesture Recognizer akár 11 egyidejű érintést is feldolgoz iPaden és 5-öt iPhone-on. Az IT Sectr-nél az UIGestureRecognizer-t használjuk a felhasználói bevitel feldolgozásának standard módjaként minden UIKit projektben — ez kiküszöbölte a touchesBegan manuális követésével kapcsolatos hibákat.
Minden UIGestureRecognizer 7 lehetséges állapoton megy keresztül, amelyek az UIGestureRecognizer.State enum-ban vannak meghatározva. Ezek az állapotok a felismerés életciklusát tükrözik: az érintés érzékelésétől a gesztus befejezéséig vagy megszakításáig. Az állapotok megértése kritikus az egyedi felismerők implementálásához és a konfliktusok hibakereséséhez.
| Állapot | Jelentés | Mikor keletkezik |
|---|---|---|
| .possible | Kezdeti állapot, a gesztus még nem ismert fel | Közvetlenül a nézethez adás után |
| .began | A gesztus felismerve és elkezdődött | Az ujj első mozgásakor pan/longPress esetén |
| .changed | A gesztus paraméterei megváltoztak (koordináták, szög) | Minden ujjmozdulatnál |
| .ended | A felhasználó felemelte az ujját, a gesztus befejeződött | touchesEnded-nél |
| .cancelled | A gesztus megszakadt a rendszer által (bejövő hívás, tájolásváltás) | touchesCancelled-nél |
| .failed | A gesztus nem ismert fel a feltételek szerint | touchesCancelled-nél felismerés nélkül |
| .recognized | .ended szinonimája; a gesztus sikeresen felismerve | Ugyanaz, mint .ended |
A diszkrét gesztusok (tap, swipe) .possible-ból közvetlenül .ended vagy .failed állapotba kerülnek. A folyamatos gesztusok (pan, pinch, rotation, longPress) .possible → .began → .changed (többször) → .ended útvonalon haladnak. Az akció metódusban ellenőrizze a gestureRecognizer.state-et — ez lehetővé teszi a gesztus kezdetének, változásának és végének megkülönböztetését.
Az UIKit 7 beépített UIGestureRecognizer alosztályt kínál, amelyek a legtöbb interakciós forgatókönyvet lefedik. Minden alosztály rendelkezik specifikus beállításokkal: numberOfTapsRequired a taphoz, direction a swipchez, minimumPressDuration a hosszú nyomáshoz.
Egyedi gesztusokhoz (például cikcakk rajzolása) az UIGestureRecognizer alosztálya jön létre a touchesBegan, touchesMoved, touchesEnded metódusok felülírásával és az állapot frissítésével. Az Apple ajánlja a beépített osztályok használatát, ahol lehetséges — ezek optimalizáltak és helyesen működnek együtt.
Amikor egy UIView-en több UIGestureRecognizer-t használnak (pl. tap és double-tap), felismerési konfliktus alakul ki: dupla tapnál előbb az egyszeres tap aktiválódik. Megoldásként az Apple a require(toFail:) metódust kínálja, amely késlelteti az egyik gesztus felismerését, amíg a másik meg nem hiúsul.
A mechanizmus a következőképpen működik: a tapRecognizer.require(toFail: doubleTapRecognizer) hívásával megadja, hogy a tapRecogniser csak azután lép .recognized állapotba, miután a doubleTapRecogniser .failed állapotban befejeződött. Ez ~0,3 másodperc késleltetést ad az egyszeres tap végrehajtása előtt — a felhasználó kétszer koppint, az első tap figyelmen kívül marad. Alternatív módszer — az UIGestureRecognizerDelegate delegált a gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:) metódussal, amely engedélyezi az egyidejű felismerést (pl. pan + pinch térképhez).
A WWDC 2020 szerint a UIKit alkalmazásokban a hibák körülbelül 15%-a a gesztuskonfliktusok helytelen beállításához kapcsolódik. Az IT Sectr-nél szabványosítottuk a megközelítést: minden képernyőhöz egy gesztusséma készül require(toFail:) prioritásokkal — ez teljesen kiküszöbölte a tapek dupla aktiválódásával kapcsolatos hibákat.
Egyszeres tap kezelőt ad a UIImageView-hez. Koppintáskor a kép megváltoztatja az átlátszóságát — a legegyszerűbb példa, amely bemutatja a gesztus nézethez kapcsolását.
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
}
}
}
Kulcsfontosságú pont: az isUserInteractionEnabled a UIImageView-n alapértelmezetten false — e zászló nélkül a Gesture Recognizer nem fog érintéseket kapni. UIView és UIButton esetén a zászló alapértelmezetten be van kapcsolva. Az akció metódus egy UITapGestureRecognizer paramétert fogad, amelyen keresztül a location(in:) elérhető a tap koordinátáinak meghatározásához.
Balra swipet implementál az előző képernyőre való visszatéréshez. Bemutatja az irány beállítását és a gesztus hozzárendelését a vezérlő gyökér nézetéhez.
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)
}
}
Az UISwipeGestureRecognizer — diszkrét gesztus: felismerés után azonnal .recognised állapotba kerül, köztes .changed nélkül. Ezért az akcióban nem kell ellenőrizni az állapotot — a gesztus vagy felismerésre kerül (akció meghívódik), vagy nem. A direction tulajdonság négy érték egyikét fogadja: .left, .right, .up, .down. Több irány támogatásához hozzon létre külön felismerőket minden irányhoz.
Megmutatja, hogyan konfigurálható az egyszeres és dupla tap ugyanazon a nézeten konfliktus nélkül. A dupla tap felismerőjének van prioritása — az egyszeres tap csak akkor aktiválódik, ha a dupla tap nem ismert fel.
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")
}
}
A require(toFail:) nélkül dupla tapnál először a handleSingleTap hívódik meg, majd a handleDoubleTap — ez rontja a UX-et. A require(toFail:) segítségével az egyszeres tap ~0,3 másodpercet vár, hogy megbizonyosodjon arról, hogy nem következik be a második tap. Az IT Sectr-nél ezt a mintát képszerkesztőkben és galériákban használják, ahol a dupla tap nagyít, az egyszeres tap pedig kijelöl egy elemet.
Gyakran ismételt kérdések
Igen, a UIView egyszerre több UIGestureRecognizer-t támogat. A konfliktusok feloldásához használja a require(toFail:) metódust, amely meghatározza a felismerés sorrendjét. A gesztusok párhuzamos működéséhez (pl. pan + pinch térképen) implementálja a gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:) delegáltat, amely true értéket ad vissza.
UIGestureRecognizer — magas szintű absztrakció, amely automatikusan ismeri fel az érintési mintákat és kezeli az állapotokat. A touchesBegan — a UIResponder alacsony szintű metódusa, amely a koordináták, időzítés és érintések megszakításának manuális követését igényli. A Gesture Recognizer egyszerűbb, megbízhatóbb és előnyösebb a szabványos gesztusokhoz; a touchesBegan csak egyedi grafikához indokolt.
A natív SwiftUI-ban módosítók használatosak: onTapGesture, onLongPressGesture, DragGesture, MagnificationGesture, RotationGesture. Ezek az UIGestureRecognizer deklaratív megfelelői, integrálva a SwiftUI hierarchiába. Szükség esetén becsomagolható az UIKit felismerő UIViewRepresentable segítségével, de az Apple a natív SwiftUI gesztusok használatát ajánlja.
Összefoglalás
Kulcsrakész mobilalkalmazást fejlesztünk
Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.
Olvassa el is