Puntos clave
El deslizamiento es un movimiento rápido y deslizante del dedo sobre la superficie de la pantalla que termina con el levantamiento. Puede ser corto (pasar un elemento) o largo (abrir una cortina), pero la característica clave es la discreción: el sistema lo reconoce como un único evento, no como una secuencia continua de coordenadas.
Los sistemas operativos móviles detectan un deslizamiento según tres parámetros: distancia mínima (umbral), velocidad mínima (velocity) y dirección. Si el dedo se mueve lentamente o la distancia es insuficiente, el sistema interpreta el gesto como panorámica o toque. Los valores umbral difieren entre plataformas: iOS usa una constante en UISwipeGestureRecognizer con un umbral fijo, Android usa ViewConfiguration.getScaledTouchSlop(), que devuelve ~8–16 dp según la densidad de pantalla.
Según la Documentación para desarrolladores de Android, onFling (swipe) en GestureDetector se activa a velocidades superiores a 100 px/s y distancias desde 50px. En IT Sectr, usamos el deslizamiento para navegación (galerías, interfaces de tarjetas, incorporación) — este gesto es intuitivo para usuarios de todas las edades.
El deslizamiento y la panorámica (pan/drag) son tipos diferentes de gestos, aunque ambos implican mover el dedo por la pantalla. El deslizamiento es discreto y significa "realizar una acción", mientras que la panorámica es continua y significa "mover un objeto mientras el dedo está en la pantalla".
| Parámetro | Swipe | Pan/Drag |
|---|---|---|
| Tipo | Discreto | Continuo |
| Eventos | Único (reconocido/no reconocido) | Múltiples (.began → .changed → .ended) |
| Velocidad | Alta (movimiento rápido) | Cualquiera (movimiento lento) |
| Ejemplo iOS | UISwipeGestureRecognizer | UIPanGestureRecognizer |
| Ejemplo Android | GestureDetector.onFling() | GestureDetector.onScroll() |
| Escenario | Eliminar un correo, pasar página | Mover un mapa, arrastrar un archivo |
| Seguimiento de coordenadas | No (solo dirección) | Sí (actualización constante de coordenadas) |
La elección entre deslizamiento y panorámica depende de la tarea. Para acciones sobre un elemento (eliminar, archivar, abrir menú) usa deslizamiento. Para manipulación directa (mover, escalar) usa panorámica. Mezclar gestos en el mismo elemento requiere require(toFail:) o lógica personalizada.
UISwipeGestureRecognizer es una subclase de UIGestureRecognizer que reconoce un deslizamiento discreto en una de cuatro direcciones. A diferencia de UIPanGestureRecognizer, no genera eventos .changed intermedios: al ser reconocido, la acción se llama exactamente una vez.
La propiedad direction (UISwipeGestureRecognizer.Direction) establece la dirección rastreada — .left, .right, .up, .down. Se pueden combinar direcciones mediante una máscara: [.left, .right] para deslizamientos horizontales. La propiedad numberOfTouchesRequired determina el número de dedos — 1 por defecto. UISwipeGestureRecognizer no tiene un umbral de distancia configurable — iOS usa un valor incorporado de aproximadamente 20px, suficiente para un reconocimiento fiable.
Dado que UISwipeGestureRecognizer es un reconocedor discreto, no entra en conflicto con UIPanGestureRecognizer: el movimiento lento activa pan, el movimiento rápido activa swipe. Sin embargo, al coexistir con UITapGestureRecognizer en la misma vista, puede ser necesario require(toFail:) para evitar el reconocimiento falso de toque.
En Android, la API principal para el reconocimiento de deslizamientos es GestureDetector con el callback OnGestureListener, específicamente el método onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY). GestureDetector analiza la posición inicial y final del dedo, la velocidad de movimiento en los ejes y la distancia, y luego llama a onFling cuando se superan los umbrales.
Los umbrales mínimos se configuran mediante ViewConfiguration: VelocityTracker.getMinVelocity() (~100 px/s) y ViewConfiguration.getScaledTouchSlop() (~8–16 dp). Un OnSwipeTouchListener personalizado es un patrón común que simplifica la API: el desarrollador no necesita calcular manualmente la dirección y la velocidad. La Android Support Library no incluye un "SwipeDetector" incorporado, por lo que un envoltorio personalizado es la solución estándar.
Para deslizar en listas, Google recomienda usar ItemTouchHelper.SimpleCallback de RecyclerView, que abstrae la detección de gestos, la animación y los callbacks onSwiped(). En IT Sectr, usamos un SwipeListener personalizado para ViewPager2 e interfaces de tarjetas — esto da control sobre la sensibilidad del deslizamiento sin sacrificar el rendimiento.
Para listas (UITableView, RecyclerView), Apple y Google proporcionan APIs especializadas que reemplazan la adición manual de UISwipeGestureRecognizer. Estas APIs admiten botones de acción, animación personalizada y gestos de "deslizar para eliminar" — patrones estándar en aplicaciones móviles.
En iOS (11+), se usa UISwipeActionsConfiguration: el método tableView(_:leadingSwipeActionsConfigurationForRowAt:) devuelve un array de UIContextualAction — botones con título, estilo (destructive, normal) y manejador. Por ejemplo, "Eliminar" — .destructive con fondo rojo, "Archivar" — .normal con fondo gris. El sistema anima la aparición de los botones y llama al bloque completion después de un toque.
En Android, ItemTouchHelper.SimpleCallback se adjunta a RecyclerView mediante ItemTouchHelper.attachToRecyclerView(). Los parámetros incluyen banderas de dirección (start, end, up, down) y el callback onSwiped(viewHolder, direction). Dentro de onSwiped, se llama a adapter.notifyItemRemoved(position) para eliminar el elemento con animación. ItemTouchHelper maneja automáticamente los gestos, conflictos de desplazamiento y devolución del elemento si el deslizamiento no se completa. Según Google I/O 2024, ItemTouchHelper se usa en el 70% de las aplicaciones Android con acciones de deslizamiento en listas.
Añade UISwipeGestureRecognizer a una vista para volver a la pantalla anterior. El código muestra la configuración mínima del reconocedor con la dirección .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)
}
}
}
El reconocedor se adjunta a la vista raíz del controlador. La comprobación navigationController.viewControllers.count garantiza que pop solo se ejecute cuando haya una pantalla anterior en la pila. Para controladores modales, se usa dismiss. El código no requiere comprobación de estado — UISwipeGestureRecognizer es discreto y llama a la acción solo tras el reconocimiento exitoso.
Implementa un envoltorio simple de GestureDetector que determina la dirección del deslizamiento y llama a los callbacks 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 extiende SimpleOnGestureListener, implementando onFling. diffX/diffY determinan la dirección, SWIPE_THRESHOLD (100px) y SWIPE_VELOCITY_THRESHOLD (100 px/s) son los umbrales. Uso: view.setOnTouchListener(OnSwipeTouchListener(context).apply { onSwipeLeft = { dismiss() } }).
Añade un botón de eliminar al deslizar a la izquierda sobre una celda de la tabla. Usa la API integrada de iOS 11+ sin reconocimiento manual de gestos.
func tableView(
_ tableView: UITableView,
trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath
) -> UISwipeActionsConfiguration? {
let deleteAction = UIContextualAction(
style: .destructive,
title: "Eliminar"
) { _, _, 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
}
El método devuelve UISwipeActionsConfiguration con un array de UIContextualAction. style: .destructive establece automáticamente el fondo rojo. performsFirstActionWithFullSwipe = true permite ejecutar la acción con un deslizamiento completo sin toque adicional. En IT Sectr, usamos esta API para todas las listas con acciones — está estandarizada, admite VoiceOver y no requiere GestureRecognizers personalizados.
Preguntas frecuentes
El deslizamiento es un gesto discreto con movimiento rápido y levantamiento del dedo, la panorámica es un arrastre continuo con seguimiento de coordenadas. UISwipeGestureRecognizer se activa una vez, UIPanGestureRecognizer genera .began → .changed → .ended. La elección depende del escenario: deslizamiento para acciones (eliminar, pasar página), pan para movimiento (mapa, deslizador).
La lógica del gesto es idéntica, pero la API difiere. iOS usa UISwipeGestureRecognizer con direction. Android usa GestureDetector.onFling() con umbrales personalizados. El resultado es el mismo UX con diferentes implementaciones. Los frameworks multiplataforma (Flutter, React Native) abstraen la diferencia mediante una API de deslizamiento unificada.
En iOS (11+), usa UISwipeActionsConfiguration en UITableViewDelegate. En Android, usa ItemTouchHelper.SimpleCallback para RecyclerView. Estas APIs reemplazan los GestureRecognizers manuales, admiten botones de acción y animación, manejan correctamente conflictos de desplazamiento y se adaptan a las HIG de la plataforma.
Resumen
Desarrollaremos una aplicación móvil llave en mano
IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.
Lea también