Si eres un botón, solo tienes una tarea
(unsung.aresluna.org)- Incluso un botón simple, como el de rotar una foto, puede cambiar mucho la experiencia de usuario según cómo maneje las entradas repetidas rápidas
- El iPhone recuerda las entradas de rotación de 90 grados pulsadas varias veces y ejecuta la siguiente rotación en cuanto termina la animación
- Nothing Phone/Android confirma el toque con háptica y sonido, pero ignora los toques posteriores que llegan durante la animación de rotación
- En situaciones donde hay que rotar varias fotos de documentos, incluso una UI casual de edición de fotos necesita operaciones más rápidas y predecibles
- Un botón debería aceptar la entrada mediante buffering de toques o interrumpiendo/acelerando la animación, en vez de hacer que el usuario espere hasta que termine
Cómo el botón de rotar fotos maneja las entradas repetidas
- El botón de rotar fotos es un control estándar que, al pulsarlo una vez, normalmente hace una rotación de 90 grados en sentido antihorario, y al pulsarlo de nuevo repite la misma acción
- La animación de rotación muestra el proceso de cambio de orientación de la foto y ayuda a que el usuario no pierda el sentido de la dirección
- Si se pulsa rápidamente la rotación de 90 grados 8 veces, como diría un ingeniero, debería ser un “no op”: la imagen debería dar dos vueltas y volver a su orientación original
- En el iPhone, esa expectativa efectivamente se cumple
- Recuerda las entradas de toques rápidos
- Cuando termina la rotación anterior, ejecuta la siguiente que estaba en espera
- En Nothing Phone/Android, el resultado es distinto
- El botón confirma el toque con háptica y sonido
- Si la animación de rotación anterior no terminó, ignora el nuevo toque
Incluso una UI casual necesita un manejo de entrada más sólido en tareas repetitivas
- La discapacidad situacional es la idea de que la discapacidad no le ocurre solo a algunas personas, sino que cualquiera puede experimentarla de forma efectiva en una situación determinada
- Tomar fotos suele acercarse a un uso casual, pero la cámara del teléfono puede confundirse con la orientación de la foto cuando se toma apuntando hacia abajo
- Si después de tomar varias fotos horizontales de documentos hay que rotar cada una, el usuario puede terminar teniendo que rotar decenas de fotos una por una
- En una situación así, es más agradable y menos engorroso que el botón funcione de forma predecible incluso si se pulsa tres veces a la velocidad deseada
- Si se mira un periodo suficientemente largo o un grupo de usuarios suficientemente grande, incluso las interfaces casuales encuentran momentos en los que deben tratarse con más seriedad
- Este tipo de “usuario avanzado situacional” también aparece en casos como la edición de fotos en el teléfono o GarageBand
- La solución no necesariamente tiene que ser el buffering de toques
- Si entra un toque interruptivo, también se podría detener o acelerar la animación
- La clave es no obligar al usuario a esperar a que termine la animación
2 comentarios
Opiniones de Hacker News
¿Qué pasa cuando el usuario hace demasiados clics por error, o cree que el primer clic no se registró?
Todavía me acuerdo de una keynote en la que Steve Jobs demostraba lo mucho más rápido que se habían vuelto los documentos PDF en el nuevo macOS. Los ingenieros habían preparado un botón que desplazaba el PDF en pantalla, pero Jobs lo apretó por accidente más de una vez y dijo: “¿va a desplazarse hasta el final dos veces?”. Y efectivamente, la acción se puso en búfer. Al final tuvo que volver a subir y esperar a que terminara el segundo desplazamiento
Jobs lo arregló diciendo que “incluso contando el tiempo de recorrer el documento por segunda vez, sigue siendo más rápido que los PDF del sistema operativo anterior”
El debouncing existe por una razón. Hay veces en que, si presionas un botón dos veces, debe ejecutarse dos veces, y otras en que no debería. No es sencillo distinguir qué opción conviene en cada situación
Como mínimo, hay que pensar qué manejo corresponde en cada caso. Dentro de una UI, para algunos botones una opción es obvia y para otros es claramente la contraria, pero algunos quedan en una zona gris; si ambos comportamientos son defendibles, al final terminas con una UI inconsistente
No sé bien cuál sería una buena solución
Según el artículo:
Ese es el problema. La cantidad de veces que la app le indica al usuario que se registró la pulsación del botón debe ser igual a la cantidad de acciones realmente ejecutadas. El debouncing es una buena práctica, pero si lo vas a usar, no debes dar feedback para los toques descartados por debouncing
Me confundió bastante que saliera el tema de accesibilidad en la parte de “¿qué pasa si el usuario hace demasiados clics por error, o cree que el primer clic no se registró?”. Porque inmediatamente pensé en alguien con temblores en las manos que intenta presionar una sola vez y termina presionando dos veces
Y también hay personas que hacen doble clic en todos los botones. Para manejar esos casos, es muy común desactivar el botón de enviar dentro de
onclickYa no soy fanboy de Apple/Mac/Jobs como antes, pero extraño las presentaciones de Apple que parecían estar conducidas por alguien que quería mostrar algo genial
Las presentaciones recientes de Apple me resultan demasiado rígidas e impersonales, como si las manejaran personas de traje, al punto de que ni siquiera puedo verlas hasta el final. Todos parecen robots que esconden cualquier emoción o humanidad por miedo a que los despidan si se apartan del guion sagrado
Puede que me haya ido del tema, pero me hizo recordar lo entretenidas que eran incluso las presentaciones improvisadas y algo desordenadas de Jobs
Esta situación claramente no sirve como “¿y en este caso qué?”. Por dos razones:
Quiero apoyar el argumento de “¿y qué pasa con el debouncing?” que apareció en otro lado, y el autor no debería simplemente ignorarlo
Pero también me molesta el meme de you had one job, y quiero oponerme a su uso irreflexivo. La mayoría de las veces ese meme está equivocado, y con los botones en general también. En una interfaz de usuario común, un botón específico cumple varias de estas funciones a la vez:
La expresión de que un botón tiene “un solo trabajo” no encaja bien. Un botón tiene que hacer varias cosas, no solo ser clickeable
Está el feedback cuando se hace clic, el feedback al pasar el mouse, el estado de carga, el estado deshabilitado y todas las combinaciones de eso. Esto puede resultar muy frustrante. Por ejemplo, si presionas un botón conectado a un servicio backend y no hay ningún feedback visual, no sabes si el botón se presionó. Si tampoco hay estado de carga, no sabes si algo se está procesando por detrás y terminas presionándolo varias veces
El autor quiere que, si haces tres clics a cualquier velocidad, siempre se ejecute la misma función. Quiere pasar fotos rápidamente y rotarlas una por una de forma predecible, y eso es válido
El autor lo plantea con base en la accesibilidad
Pero también se puede imaginar el argumento completamente opuesto. Por ejemplo, una persona con discapacidad motriz o Parkinson podría preferir un comportamiento en el que los clics excesivos se ignoran cuando la intención ya quedó confirmada
Hacer esto bien es complicado
iOS tiene una opción de accesibilidad llamada Ignore Repeats, y como es una configuración de todo el sistema parece un mejor enfoque. Las personas que necesitan esa corrección también podrían aprovecharla en lugares como el teclado en pantalla, y no haría falta que todos los demás escriban más lento
No creo que todos los widgets de UI deban pensar hasta en esto
Parece algo que podría manejarse como una configuración de todo el dispositivo. Por ejemplo, ignorar toques repetidos dentro de 100 ms si están a menos de 50 px entre sí
Totalmente de acuerdo. Incluso sin llegar al Parkinson, al ver a personas no muy mayores pero sí de cierta edad usar teléfonos y pantallas táctiles, se nota que a menudo les cuesta presionar botones pequeños en el lugar exacto y sentir “sí, lo presioné”
Por eso, desde el punto de vista de accesibilidad, creo que el comportamiento del Nothing Phone encaja mucho mejor. Esos usuarios no hacen 6 clics contando números para poner la imagen en una posición específica. Además, tampoco tiene mucho sentido hacer 8 clics por adelantado para volver exactamente a la posición actual
La forma de pensar en este caso se parece más a “hago clic, espero, comparo si está en la posición que quiero y vuelvo a hacer clic”. En cambio, un botón sensible que bufferiza la entrada puede provocar overshooting: pasarse demasiado y luego volver demasiado. Es parecido a intentar desplazarse hasta un punto deseado en una lista y errarle una y otra vez
El enfoque del iPhone es más adecuado para gente joven o entusiastas de la tecnología que quiere operar rápido sin esperar. Es como en un teclado de computadora, donde por la latencia puedes escribir varias letras antes de que aparezca la primera en pantalla
Hace tiempo usé un dispositivo con botones físicos: tenía un botón que hacía un pitido al presionarlo y agregaba 30 segundos al tiempo. Pero a veces sonaba el pitido y no se agregaban los 30 segundos, y otras veces se agregaban 30 segundos sin sonido. Así que siempre tenía que entrecerrar los ojos para mirar la pantalla oscura y comprobar si había funcionado
Me pareció un diseño especialmente malo, pero desde entonces he visto incontables botones puramente de software que reproducen exactamente ese comportamiento roto. Que el botón cambie de color en pantalla y que la acción solicitada se ejecute de verdad solo están débilmente conectados. ¿Por qué pasa eso? ¿Cómo demonios lo implementaron?
Escribes código imperativo y das dos órdenes, y ambas pueden fallar de forma independiente
Hay muchas formas de fingir que esto ya quedó “resuelto”
Para empezar, todas las pruebas van a pasar, así que la mayoría de los desarrolladores puede dejar de pensar de inmediato
También se puede pensar que basta con capturar la excepción y dejarla en el log, pero eso no lo arregla
Se puede dejar corriendo un tiempo en producción y ver si aparece algún problema. Si finalmente aparece, el desarrollador lo intentará de nuevo, y como probablemente funcione a la segunda, otra vez puede dejar de pensar
Hace unos días también hubo una gran discusión sobre el patrón outbox. Consiste en partir la primera tarea en dos mitades y procesarla de forma atómica, dejando la segunda tarea a cargo de un lector
Creo que la razón por la que este problema sigue apareciendo en la práctica es que los desarrolladores están en una superposición cuántica entre “eso no pasa”, “ya lo arreglé” y “no se puede arreglar”
El código que cambia el color del botón es comportamiento interno del componente “botón”, así que no hay que implementarlo por separado para cada botón. Pero eso significa que está algo separado del código que realiza la acción real. Si en el handler de
on clickhay una verificación final como en el texto original, del tipo “si la animación de rotación está en curso, no rotar la imagen”, muchas veces no hay forma de decirle al botón que cancele el cambio de color. A la inversa, aunque la animación de cambio de color no se reproduzca correctamente, el handler deon clickpuede ejecutarseEl botón normalmente cambia de color cuando se presiona el botón del mouse, pero la acción se realiza al soltarlo. Este enfoque a veces da deliberadamente la oportunidad de cancelar en el último momento arrastrando fuera del botón mientras se mantiene presionado, y en móvil también permite reinterpretar la interacción como scroll en lugar de clic. En otros casos, solo aumenta las oportunidades de que la acción se bloquee después de que el color ya cambió
Esto es completamente independiente de si, en el estado actual del software, esa pulsación es una entrada útil. Un software bien escrito debería saber que no puede aceptar la entrada y desactivar el botón; incluso un software medianamente bien hecho debería ofrecer feedback casi inmediato de que está procesando la acción o de que fue cancelada
La gente suele olvidar que las animaciones son puramente auxiliares, y que la animación en sí no es el objetivo
Las animaciones existen para ocultar tiempos de carga y para suavizar el paso de un estado a otro. Por eso se usan
Ese conocimiento terminó desapareciendo, y ahora existe código que tiene que esperar a que termine la animación
Otro ejemplo sorprendente de culto al cargamento
De hecho, no estoy de acuerdo con la palabra “ocultar”. Que algo aparezca instantáneamente sin ninguna pista de por qué apareció es claramente un antipatrón en pantallas táctiles
La Apple de antes entendía que el diseño era más que hacer que algo se viera bonito. La Apple vidriosa de hoy solo se preocupa por cómo se ve en una demo
Al ver la primera comparación, honestamente pensé que el problema estaba en el ejemplo del iPhone. Cada vez que se presiona el botón de rotación, el botón y el slider debajo de la imagen desaparecen y luego vuelven a aparecer con un fade-in; es un comportamiento que he visto en varias apps de iOS y resulta infinitamente molesto. En especial, la app Screenshot es una gran frustración personal
Si es un elemento de UX que se puede manipular tanto antes como después de una interacción, debería permanecer visible durante todo el proceso, ya sea una transformación o un procesamiento. ¿Qué beneficio de UX hay en ocultar esos botones durante la rotación en el iPhone? Ni siquiera se ve mejor. Claro que últimamente el software de Apple ha venido sacrificando mejoras reales de UX en el altar de la apariencia
Dicho eso, sí estoy de acuerdo con el autor en que estos taps deberían procesarse de forma independiente de la animación
Se podría hacer algo parecido para UX. Sería una colección de restricciones de patrones de diseño que lance advertencias cuando se intenta lanzar algo con fallas de UX conocidas
Me acordé de cuando implementé BabyButton, hecho para que pudiera usarlo un bebé. En vez del comportamiento normal de clic, usé el evento
touchdown, así el niño podía ver de inmediato que algo había pasado, y no había problema si mantenía presionado el botón demasiado tiempo o movía el dedo mientras lo tenía presionadoSi deberíamos crear sistemas para ese rango de edad es un tema completamente distinto, pero sentí que fue un buen desafío de diseñar algo acorde a las necesidades del usuario
Más del 99% de los botones en el mundo real hacen su trabajo apenas los presionas. Pero un botón que funcionara así en una GUI se sentiría raro
El artículo que realmente está más cerca del punto que quiere plantear el autor es este: https://aresluna.org/show-your-hands-honor/
Durante una llamada, si presionas el botón de encendido/apagado de pantalla del iPhone, ¿a veces termina la llamada y otras veces solo apaga la pantalla?
Todavía no entiendo el patrón, pero antes de meter el iPhone al bolsillo he presionado ese botón y a veces corté la llamada por accidente
Opiniones en Lobste.rs
Es un sarcasmo ligero, pero si vamos a hablar de buena UI/UX, un video incrustado sin controles ni indicadores es de lo peor.
Mucha gente desactiva la reproducción automática por defecto, y me quedé un buen rato viendo dos imágenes casi iguales pensando: “¿Entonces cuál es la diferencia?”
Estoy totalmente de acuerdo con “nunca hagas que el usuario espere a que termine una animación”.
Si es una app, las órdenes del usuario siempre deberían tener prioridad sobre verse bonita.
Creo que este principio también aplica a los juegos. En algunos juegos, después de abrir un menú hay un retraso muy corto antes de que se registre la entrada.
Es el tipo de problema de si, en un juego con animaciones de menú llamativas, al presionar pausa y la flecha hacia abajo rápidamente, el segundo elemento debería quedar seleccionado después de que aparezca el menú.
En los juegos, el input buffering es un tema sutil, pero en la navegación de menús casi nunca se siente mal.
El video no transmitía muy bien la información. Había que mirarlo con atención para notar el momento en que se presionaba el botón varias veces seguidas.
Yendo al punto, tampoco está tan claro que el teléfono Android esté haciendo algo realmente mal. Entiendo la frustración de querer presionar rápido 8 veces para obtener el resultado deseado, pero me recuerda a la detección de doble clic accidental en los sistemas operativos de escritorio.
Al final parece más una diferencia entre la usabilidad que imagina el fabricante y la comodidad que percibe el usuario. Si hay que ponerse estrictos, que el iPhone muestre el botón “Done” incluso durante la animación me parece más distractor.
Es difícil decir que Android esté completamente equivocado. Me ha pasado que, por una UI que responde lento, ejecuté una acción dos veces por accidente.
Quizá lo mejor sea desactivar el botón hasta que termine la animación.
“Tres clics” no funciona, y los clics adicionales durante la animación se ignoran. Hacer clic, mirar la pantalla mientras esperas a que termine la animación, volver a hacer clic y esperar otra vez es lento y exige mucha atención del usuario.
Una mejor solución parece ser dar retroalimentación inmediata en la UI. Se podría renderizar de inmediato el estado del botón indicando que el clic fue procesado, o redibujar la pantalla rápidamente de forma temporal para mostrar el progreso actual antes de reproducir la animación completa y lenta.
Estoy muy de acuerdo con todo el artículo. Yo diría que este es un caso en el que la animación viola el juramento hipocrático.
El primer principio de la animación debería ser “primero, no hacer daño”, es decir, “primero, no interferir con la entrada del usuario”.
¿Alguna vez han usado un codificador rotatorio que no alcanza a seguirte? La perilla de volumen de mi amplificador de audífonos no hace nada si la giro demasiado rápido.
Si fuera presidente por un día…
Cada vez que veo algo así pienso en qué pasaría con mi toolkit de GUI. Mi toolkit, como un comportamiento tomado prestado de la plataforma Windows, trata dos clics rápidos como un doble clic, y eso a veces me vuelve loco.
El doble clic en sí es una convención común y tiene valor como algo distinto de un clic. Idealmente, los eventos de clic deberían hacerse idempotentes para que no haya problema si ocurren dos veces. Por ejemplo, si un clic selecciona un elemento y un doble clic ejecuta la acción predeterminada, seleccionar el mismo elemento dos veces no hace daño. Pero la realidad no siempre es así.
El problema es que, si haces clic muy rápido en un botón, el botón puede ejecutarse solo una vez y luego lo siguiente convertirse en un evento de doble clic que no hace nada. Así aparece el comportamiento de que, al presionar rápido, se ignora la mitad de los clics.
Por este problema creé un booleano llamado
triggersOnMultiClick, y si se configura entrue, hace que también se emitan eventos de disparo en clics múltiples rápidos como un doble clic. Sin embargo, por compatibilidad hacia atrás dejé el valor predeterminado enfalse, pero como ya toca sacar un nuevo lanzamiento mayor, me parece válido el argumento del artículo y ahora quiero cambiar el valor predeterminado atrue.Podría dejar algo como: “Agregado el 2 de julio de 2021, valor predeterminado
false. El 5 de julio de 2026 (v13.0) cambiamos el valor predeterminado atruepara que todos los clics de los botones disparen la acción por defecto. Si quieres el comportamiento anterior, sobrescribe el evento de doble clic o establecetriggersOnMultiClick = falsedespués de llamar al constructor superior”.En la implementación de animaciones puede surgir por accidente un comportamiento parecido. Si un objeto tiene una variable de posición de animación y al hacer clic se le asigna un valor, al hacer doble clic hay básicamente dos opciones: saltarse la animación restante y pasar de inmediato al siguiente estado interno, o ignorar el evento para preservar la suavidad visual, con algo como
if(animationCounter) return;. Parece que el desarrollador de Android del artículo eligió lo segundo.En el lado de Apple probablemente usaron una tercera vía: tener una cola de animaciones pendientes y poner los eventos ahí. Lo que me da curiosidad es si el estado interno cambia al encolarlo, al completarse, o si de verdad rotan la imagen mediante animación. Si interrumpes la animación a la mitad y guardas, ¿podrías terminar con una imagen rotada 45 grados? Si yo lo hiciera, actualizaría el EXIF de forma atómica en 90 grados y ejecutaría la animación de transición por separado.
Puede haber varias opciones razonables entre simplicidad de implementación, fidelidad visual y comportamiento de la UI. Personalmente, también me gusta la opción de que la segunda pulsación no espere la animación y haga snap de inmediato al nuevo estado. No creo que llegaría a crear una cola de animaciones.
Me da risa cambiar el código en fechas parecidas. El cambio anterior también fue el 2 de julio y este, por casualidad, es el 5 de julio. Me gusta escribir ese tipo de historial directamente en los comentarios de documentación. Más adelante, cuando vuelva a cambiarlo, también sirve para leerlo como recuerdo.
Apple probablemente ofrece una cola de animaciones predeterminada, pero el momento del cambio de estado sigue siendo un problema aparte. En la web, probablemente actualizaría el estado de forma atómica y dejaría aparte una animación puramente visual, como una transición CSS de
transform. También evitaría recalcular el layout, así que sería fácil y eficiente de implementar. Más adelante me gustaría experimentar si, al cambiar nombres de clase más rápido que la animación, salta o se encola.Quizá sea por la edad, pero varias veces me ha molestado este comportamiento cuando toqué dos veces por accidente.
Usar un botón en el mismo lugar o un doble toque como mecanismo de confirmación es horrible. Si de verdad hace falta confirmación, no debería ser fácil confirmar por accidente.
Dicho eso, en este ejemplo sí parece buena idea aceptar entradas rápidas consecutivas. En otros casos puede no serlo. Entre los botones físicos hay algunos que impiden intencionalmente presionar dos veces hasta que algo “termine”, y aunque no siempre sea un diseño consciente, a veces es la decisión correcta.
También hay mucha gente que tiene que usar dispositivos lentos o dañados, y si alguien tiene problemas de salud como temblores en las manos o los dedos, limitar la entrada puede tener ventajas.
Este ejemplo parece un caso en el que ambas posturas pueden ser razonables. Hay ejemplos en los que esperar es más molesto, y también hay casos en los que uno claramente desearía que existiera una limitación de entrada, ya sea por una animación o por otra condición.
Me irrita muchísimo y de forma constante que los resultados de búsqueda en navegadores móviles se actualicen bajo mi dedo y empujen a otro lugar la opción que estaba a punto de tocar.
Me he entrenado para esperar más, pero no tiene sentido que una interacción de UI tenga que ser sensible a una tarea asíncrona en segundo plano cuyo tiempo de finalización varía según el estado de la conexión.