Главно
Свипе — брзо клизање прста по површини екрана које се завршава одвајањем. Свипе може бити кратак (листање елемента) или дуг (отварање завесе), али кључна карактеристика — дискретност: систем га препознаје као један догађај, а не као непрекидни низ координата.
Мобилни ОС одређују свипе по три параметра: минимално растојање (праг), минимална брзина (velocity) и правац. Ако се прст креће споро или растојање није довољно, систем тумачи гест као панорамирање (pan) или тап. Вредности прага се разликују између платформи: иОС користи константу UISwipeGestureRecognizer са фиксним прагом, Андроид — ViewConfiguration.getScaledTouchSlop(), који враћа ~8–16 dp у зависности од густине екрана.
Према Android Developers Documentation, onFling (свипе) у GestureDetector-у се активира при брзини изнад 100 px/s и растојању од 50px. У IT Sectr-у користимо свипе за навигацију (галерије, картични интерфејси, онбординг) — овај гест је интуитивно разумљив корисницима свих узраста.
Свипе и панорамирање (pan/drag) — различити типови гестова, иако оба користе покрет прста по екрану. Свипе је дискретан и значи „уради једну радњу”, док је панорамирање непрекидно и значи „померај објекат док је прст на екрану”.
| Параметар | Свипе | Панорамирање (Pan/Drag) |
|---|---|---|
| Тип | Дискретан | Непрекидан |
| Догађаји | Један (препознат/није препознат) | Више (.began → .changed → .ended) |
| Брзина | Висока (брз покрет) | Било која (споро кретање) |
| Пример иОС | UISwipeGestureRecognizer | UIPanGestureRecognizer |
| Пример Андроид | GestureDetector.onFling() | GestureDetector.onScroll() |
| Сценарио | Обрисати имејл, прелистати страницу | Померити мапу, превући датотеку |
| Праћење координата | Не (само правац) | Да (континуирано ажурирање координата) |
Избор између свипе-а и панорамирања зависи од задатка. За радње са елементом (брисање, архивирање, отварање менија) користите свипе. За директно манипулисање (померање, скалирање) — панорамирање. Мешање гестова на истом елементу захтева require(toFail:) или прилагођену логику.
UISwipeGestureRecognizer — подкласа UIGestureRecognizer-а која препознаје дискретни свипе у једном од четири правца. За разлику од UIPanGestureRecognizer-а, не генерише међу-догађаје .changed: при препознавању action се позива тачно једном.
Својство direction (UISwipeGestureRecognizer.Direction) поставља правац праћења — .left, .right, .up, .down. Може се комбиновати кроз маску: [.left, .right] за хоризонтална превлачења. Својство numberOfTouchesRequired одређује број прстију — подразумевано 1. UISwipeGestureRecognizer нема подесиви праг растојања — иОС користи уграђену вредност од око 20px, довољну за поуздано препознавање.
Пошто је UISwipeGestureRecogniser дискретни препознавач, не конфликтује са UIPanGestureRecognizer-ом: при спором покрету активираће се pan, при брзом — swipe. Међутим, при заједничком постојању са UITapGestureRecognizer-ом на истом view-у може бити потребно require(toFail:) ради спречавања лажног препознавања тапа.
У Андроиду главни АПИ за препознавање свипе-а је GestureDetector са колбеком OnGestureListener, наиме метод onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY). GestureDetector анализира почетни и крајњи положај прста, брзину кретања по осама и растојање, након чега позива onFling при прекорачењу прагова.
Минимални прагови се подешавају кроз ViewConfiguration: VelocityTracker.getMinVelocity() (~100 px/s) и ViewConfiguration.getScaledTouchSlop() (~8–16 dp). Прилагођени OnSwipeTouchListener — распрострањени образац који поједностављује АПИ: програмер не мора ручно да израчунава правац и брзину. Android Support Library не садржи уграђени „SwipeDetector”, па је прилагођени омотач стандардно решење.
За свипе у листама Google препоручује коришћење ItemTouchHelper.SimpleCallback из RecyclerView-а, који апстрахује детекцију геста, анимацију и колбекове onSwiped(). У IT Sectr-у користимо прилагођени SwipeListener за ViewPager2 и картичне интерфејсе — то даје контролу над осетљивошћу свипе-а без утицаја на перформансе.
За листе (UITableView, RecyclerView) Apple и Google пружају специјализоване АПИ-је који замењују ручно додавање UISwipeGestureRecognizer-а. Ови АПИ-ји подржавају дугмад за радње, прилагођену анимацију и гестове „превуци да обришеш” — стандардне обрасце мобилних апликација.
У иОС-у (11+) користи се UISwipeActionsConfiguration: метод tableView(_:leadingSwipeActionsConfigurationForRowAt:) враћа низ UIContextualAction — дугмади са насловом, стилом (destructive, normal) и руковаоцем. На пример, „Обриши” — .destructive са црвеном позадином, „Архива” — .normal са сивом. Систем сама анимира појаву дугмади и позива completion блок након притиска.
У Андроиду ItemTouchHelper.SimpleCallback се прикачи на RecyclerView кроз ItemTouchHelper.attachToRecyclerView(). Параметри — заставице праваца (start, end, up, down) и колбек onSwiped(viewHolder, direction). Унутар onSwiped позива се adapter.notifyItemRemoved(position) за брисање елемента са анимацијом. ItemTouchHelper аутоматски обрађује гестове, конфликте са скроловањем и враћање елемента ако свипе није завршен. Према Google I/O 2024, ItemTouchHelper се користи у 70% Андроид апликација са свипе радњама у листама.
Додаје UISwipeGestureRecognizer на view за повратак на претходни екран. Код показује минималну конфигурацију препознавача са правцем .left.
import UIKit
class CardViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let swipeLeft = UISwipeGestureRecognizer(
target: self,
action: #selector(handleSwipeLeft(_:))
)
swipeLeft.direction = .left
swipeLeft.numberOfTouchesRequired = 1
view.addGestureRecognizer(swipeLeft)
}
@objc private func handleSwipeLeft(_: UISwipeGestureRecognizer) {
guard let nav = navigationController else { return }
if nav.viewControllers.count > 1 {
nav.popViewController(animated: true)
} else {
nav.dismiss(animated: true)
}
}
}
Препознавач се везује за root-view контролера. Провера navigationController.viewControllers.count гарантује да се pop извршава само када постоји претходни екран у стеку. За модалне контролере користи се dismiss. Код не захтева проверу state — UISwipeGestureRecognizer је дискретан и позива action само при успешном препознавању.
Имплементира једноставан омотач GestureDetector-а који одређује правац превлачења и позива колбекове onSwipeLeft/Right.
import android.content.Context
import android.view.GestureDetector
import android.view.MotionEvent
import android.view.View
open class OnSwipeTouchListener(context: Context) : View.OnTouchListener {
private val gestureDetector = GestureDetector(context, GestureListener())
override fun onTouch(v: View, event: MotionEvent): Boolean {
return gestureDetector.onTouchEvent(event)
}
private inner class GestureListener : GestureDetector.SimpleOnGestureListener() {
companion object {
private const val SWIPE_THRESHOLD = 100
private const val SWIPE_VELOCITY_THRESHOLD = 100
}
override fun onFling(
e1: MotionEvent?,
e2: MotionEvent,
velocityX: Float,
velocityY: Float
): Boolean {
val diffX = e2.x - e1!!.x
val diffY = e2.y - e1.y
if (Math.abs(diffX) > Math.abs(diffY)) {
if (Math.abs(diffX) > SWIPE_THRESHOLD
&& Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD
) {
if (diffX > 0) onSwipeRight()
else onSwipeLeft()
return true
}
} else {
if (Math.abs(diffY) > SWIPE_THRESHOLD
&& Math.abs(velocityY) > SWIPE_VELOCITY_THRESHOLD
) {
if (diffY > 0) onSwipeDown()
else onSwipeUp()
return true
}
}
return false
}
}
open fun onSwipeLeft() {}
open fun onSwipeRight() {}
open fun onSwipeUp() {}
open fun onSwipeDown() {}
}
OnSwipeTouchListener проширује SimpleOnGestureListener, имплементирајући onFling. diffX/diffY одређују правац, SWIPE_THRESHOLD (100px) и SWIPE_VELOCITY_THRESHOLD (100 px/s) — прагови. Коришћење: view.setOnTouchListener(OnSwipeTouchListener(context).apply { onSwipeLeft = { dismiss() } }).
Додаје дугме за брисање при превлачењу улево по ћелији табеле. Користи уграђени АПИ иОС 11+ без ручног препознавања гестова.
func tableView(
_ tableView: UITableView,
trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath
) -> UISwipeActionsConfiguration? {
let deleteAction = UIContextualAction(
style: .destructive,
title: "Обриши"
) { _, _, completionHandler in
items.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .automatic)
completionHandler(true)
}
deleteAction.backgroundColor = .systemRed
let config = UISwipeActionsConfiguration(actions: [deleteAction])
config.performsFirstActionWithFullSwipe = true
return config
}
Метод враћа UISwipeActionsConfiguration са низом UIContextualAction. style: .destructive аутоматски поставља црвену позадину. performsFirstActionWithFullSwipe = true омогућава извршење радње потпуним превлачењем без додатног притиска. У IT Sectr-у користимо овај АПИ за све листе са радњама — стандардизован је, подржава VoiceOver и не захтева прилагођене GestureRecognizer-е.
Често постављана питања
Свипе — дискретни гест са брзим покретом и одвајањем прста, панорамирање — непрекидно превлачење са праћењем координата. UISwipeGestureRecognizer се активира једном, UIPanGestureRecognizer генерише .began → .changed → .ended. Избор зависи од сценарија: свипе — за радње (обрисати, прелистати), pan — за померање (мапа, клизач).
Логика геста је идентична, али се АПИ разликује. иОС користи UISwipeGestureRecognizer са direction. Андроид користи GestureDetector.onFling() са прилагођеним праговима. Резултат — исти UX са различитом имплементацијом. Вишеплатформски оквири (Flutter, React Native) апстрахују разлику кроз јединствени АПИ за свипе.
У иОС-у (11+) користите UISwipeActionsConfiguration у UITableViewDelegate-у. У Андроиду — ItemTouchHelper.SimpleCallback за RecyclerView. Ови АПИ-ји замењују ручне GestureRecognizer-е, подржавају дугмад за радње и анимацију, исправно обрађују конфликт са скроловањем и прилагођавају се платформским HIG смерницама.
Закључци
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође