Главне тачке
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 пролази кроз 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 — то омогућава разликовање почетка, промене и краја геста.
UIKit пружа 7 уграђених подкласа UIGestureRecognizer, које покривају већину сценарија интеракције. Свака подкласа има специфична подешавања: numberOfTapsRequired за тап, direction за превлачење, minimumPressDuration за дуго притискање.
За прилагођене гестове (на пример, цртање цик-цак) креира се подкласа UIGestureRecognizer са преписивањем метода touchesBegan, touchesMoved, touchesEnded и ажурирањем стања. Apple препоручује коришћење уграђених класа где год је могуће — one су оптимизоване и правилно интерагују једна са другом.
Када се на истом 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:) — ово је потпуно елиминисало грешке са двоструким активирањем тапова.
Додаје руковалац појединачним тапом на UIImageView. При тапу слика мења провидност — најједноставнији пример који показује повезивање геста са приказом.
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:) за одређивање координата тапа.
Имплементира превлачење улево за повратак на претходни екран. Демонстрира подешавање правца и повезивање геста са кореним приказом контролера.
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. За подршку више праваца креирајте засебне препознаваче за сваки.
Показује како подесити појединачни и двоструки тап на истом приказу без сукоба. Препознавач двоструког тапа има приоритет — појединачни тап се активира само ако двоструки није препознат.
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 — то квари UX. Са require(toFail:) појединачни тап чека ~0.3 секунде да би се уверио да други тап неће уследити. У IT Sectr овај образац се користи у уређивачима слика и галеријама, где двоструки тап увећава, а појединачни — бира елемент.
Често постављана питања
Да, UIView подржава више UIGestureRecognizer истовремено. За решавање сукоба користите метод require(toFail:), који поставља редослед препознавања. За паралелни рад гестова (на пример, pan + pinch на мапи) имплементирајте делегата gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:), који враћа true.
UIGestureRecognizer — апстракција високог нивоа која аутоматски препознаје обрасце додира и управља стањима. touchesBegan — метод ниског нивоа UIResponder који захтева ручно праћење координата, тајминга и отказивање додира. Gesture Recognizer је једноставнији, поузданији и пожељнији за стандардне гестове; touchesBegan је оправдан само за прилагођену графику.
У изворном SwiftUI користе се модификатори: onTapGesture, onLongPressGesture, DragGesture, MagnificationGesture, RotationGesture. То су декларативни аналоги UIGestureRecognizer-а, интегрисани у SwiftUI хијерархију. По потреби можете умотати UIKit препознавач кроз UIViewRepresentable, али Apple препоручује коришћење изворних SwiftUI гестова.
Резиме
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође