Hola a todos! Recientemente, hubo un concurso del VKontakte Mobile Challenge, y mi trabajo ganó un premio. Siguiendo las instrucciones de la segunda etapa, fue necesario desarrollar una fuente de noticias para dispositivos móviles, y los principales criterios de evaluación fueron el desplazamiento suave y la carga posterior. Cuando participé, decidí que, independientemente del resultado final, intentaré escribir un artículo sobre el enfoque para la implementación de la cinta y sobre mis emociones y sentimientos durante el concurso. Lo cual hice. Debajo del gato, consejos y trucos para desarrollar fuentes de noticias en modo narración.

Sobre el concurso
En primer lugar, vale la pena contar un poco sobre la competencia. Como sé, VKontakte organiza anualmente tales eventos para desarrolladores móviles. Yo mismo ya participé en 2012 y 2013. Las tareas fueron el desarrollo de chat y filtros de imagen, respectivamente. En 2013, logré llegar a la ronda final y ganar 100,000 rublos, lo que luego me pareció una muy buena cantidad por 4 días de trabajo interesante.
Y, al ver los anuncios en una red social, decidí que tenía que intentarlo, porque lo bueno es demostrarte a ti mismo que aún puedes escribir código de alta calidad en muy poco tiempo).
Hubo 2 etapas en la competencia: en la primera, se propuso aprobar una prueba de 30 preguntas, resolver 2 problemas de olimpiada y uno de alta calidad (es decir, una solución es un conjunto de pensamientos en forma de texto). En la segunda ronda de más de 1000 personas, solo pasaron 112 (estamos hablando solo de iOS, en Android un poco más bajos) y comenzó la etapa principal: el desarrollo de aplicaciones.
En la asignación, tenía que hacer un feed de noticias, escribir la integración con la API de VK usted mismo, el feed debería haber sido muy fluido en todos los dispositivos, había ciertos requisitos para mostrar contenido (publicaciones con imágenes y un carrusel, contadores de me gusta, compartidos, número de vistas, así como la capacidad de minimizar y desplegar la publicación bajo ciertas condiciones).
Los diseños se presentaron en Figma. Esta es una buena opción para la competencia: debido a la cantidad de usuarios activos, la competencia se siente de inmediato (e incluso se eliminó el sábado, porque se excedió el límite simultáneo de 50 conexiones).
En general, la organización de la competencia fue fluida, excepto por algunos puntos: inmediatamente después de la publicación de la tarea, el enlace a los diseños resultó estar roto, luego, como dije anteriormente, el sábado se bloqueó la entrada a Figma (este problema se reparó muy rápidamente), bueno, después del evento, tardó bastante tiempo esperar resultados Pero todo lo negativo suaviza una publicación similar:
Plan de Victoria y Estrategia
Un bloque muy importante e interesante si participas en competiciones o te gustaría probar. Antes de participar, recomiendo a todos que piensen en el objetivo que persiguen y el resultado que desean obtener al salir. Antes de comenzar el concurso, decidí por mí mismo que quería ganarlo (es decir, tomar el premio). Y para hacer esto, necesitas una estrategia:
La siguiente es la fórmula para una estrategia ganadora (me parece universal y simple, pero estoy seguro de que la mayoría de los participantes no la siguen):
- Lee la tarea con mucho cuidado . Después de unas horas, léelo nuevamente (me encontré con una situación varias veces cuando no percibía las tareas la primera vez). Y un par de horas antes de la entrega una vez más por si acaso.
- Redacte preguntas y pregúnteles a los organizadores (por lo general, los requisitos para la tarea no tienen en cuenta muchas situaciones o están expuestos de manera muy superficial). Hice exactamente eso, obteniendo respuestas al estilo de "su elección".
- Haz un plan de proyecto. Sí, la participación en una competencia o hackathon es un proyecto. El proyecto tiene una meta, cronograma, recursos y presupuesto. El tiempo es estrictamente limitado y es imposible mover el final del cambio. No se puede invitar a personas adicionales: la competencia no es una competencia de equipo. Todo lo que tienes es tu propia gente. / horas Determine de inmediato cuánto está dispuesto a gastar (piense bien cuánto va a dormir, comida, castración, descansos, y sí, su productividad disminuirá debido a la fatiga y los nervios de una sensación de impotencia frente a un marco de tiempo inexorablemente cercano).
- El plan debe tener tareas, sus prioridades y pesos (utilicé estimaciones de los formularios 1, 2, 4, 8). La evaluación incluye la duración, la falta de voluntad para cumplirla y los riesgos potenciales (complejidad, condiciones incomprensibles, falta de experiencia en dicho desarrollo, etc.). A continuación, elabora un plan detallado (o tareas de hoja de ruta): primero, el más importante y complejo (déjelo fácil y comprensible al final).
- Elija algunos intervalos grandes o hitos (los días son geniales, tuvimos 3 días). Y a medida que avanza en cada uno de ellos, mire su plan de trabajo, descomponga las tareas, ordénelas y, lo más importante, tache las que ya ha hecho con placer.
Ahora el consejo (acabo de hacer eso): mire cuidadosamente cómo se formula la tarea, si contiene las palabras "requerido" y "adicionalmente". ¿Y cuáles son los criterios de evaluación para los elementos del "adicional", es decir? si anularán su falta de requisitos implementados en la sección "obligatoria". Lo dudo Por lo tanto, concéntrese en la sección "requerida". Por mi parte, generalmente taché deliberadamente la sección "avanzada" y ni siquiera tuve la intención de iniciarla. - Al implementar, tenga cuidado con los detalles . Por ejemplo, si la tarea tiene muchos formularios de interfaz, hágalos de la forma en que se muestran en los diseños. Presta atención a las sangrías, fuentes, sombras, interlineado, etc. Esto ciertamente le agregará puntos por precisión y consistencia. Por cierto, en Figma, a diferencia de Zeplin, hay una exportación muy amplia de configuraciones, por ejemplo, puede obtener una buena NSAttributedString.
La misión se abrió la noche del jueves al viernes. El viernes es un día laboral y no hubo oportunidad ni ganas de participar en una competencia en el trabajo. Por lo tanto, después de regresar del trabajo el viernes por la noche, comencé a planificar, evaluar y calcular cuánto tiempo real puedo pasar.
El plan inicial fue el siguiente:
Día | Tiempo | Las tareas |
Viernes | 4 horas | Autorización, obtención de datos para el perfil y la cinta. |
Sabado | 9 horas | El prototipo de la cinta con la visualización de cuadrados rojos de diferentes alturas, cargándolos en la parte superior mediante extracción para actualizar y cargando sin cesar en la historia, preparando todos los modelos y servicios (trabajando con la API, el almacenamiento en caché de solicitudes, el almacenamiento en caché de imágenes).
|
Domingo | 9 horas | Salida de datos reales (texto, imágenes individuales y carruseles), contadores de me gusta, vistas y ajuste final por diseños. |
Técnica de implementación de cintas
¡Hurra! Señores, desarrolladores, han encontrado el bloque correcto. Aquí hablaremos sobre la implementación de la parte principal de la tarea: las noticias.
En general, si nos desconectamos, la fuente de noticias es solo una implementación aplicada, en general, hablaremos sobre la creación de una lista de entidades heterogéneas (es decir, cada una puede tener su propia pantalla, que requiere cálculos adicionales y su propia altura, que puede cambiar dinámicamente incluso después de que se muestre la publicación) , además, la lista puede actualizarse a través de pull-to-refresh y cargar infinitamente datos desde abajo.
Fue el problema general que decidí resolver en primer lugar. El primer paso es analizar las soluciones existentes. Debes concentrarte en el más fuerte, así que elegí 3 aplicaciones para comparar: Vkontakte, Facebook, Instagram.
Entonces, quería realizar un estudio de los 6 problemas más agudos y críticos:
- Pull-to-refresh (agregar al principio de la lista)
- Carga de historial sin problemas (agregar al final de la lista)
- Desplazamiento rápido (alternativamente con los dedos aceleramos la cinta tanto como lo permite la fuerza de fricción)
- Desplácese hacia arriba (acumule una gran historia y haga clic en la barra de estado)
- ¿Hay una divulgación dinámica (aumento) de la publicación y cuál es la animación?
- Cómo funciona la cinta en el modo de pérdida de paquetes casi completa (Desarrollador -> Acondicionador de enlace de red -> Muy mala red)

En general, todas las aplicaciones se comportan bien, pero aún noté algunos problemas.
Vea, por ejemplo, cómo se comporta el comportamiento de extracción para actualizar en VKontakte si no suelta el dedo durante la actualización y tira suavemente de la cinta hacia arriba (vea el gif a la izquierda).
¿También ves este salto?
Instagram y Facebook no mostraron tal comportamiento.
Y también hay una diferencia notable en la divulgación posterior. Para Facebook e Instagram, esto sucede con una animación suave, y VKontakte simplemente actualiza el tamaño haciendo clic.



Entonces, nuestra tarea es hacer un desplazamiento suave, descargar publicaciones y también revelar con animación.
El primer paso es elegir un concepto y crear un prototipo en cuadrados rojos (me pregunto por qué siempre elijo intuitivamente un color rojo para los prototipos. ¿Es este el caso con todos?).
Mi idea principal para mejorar la productividad fue abandonar todas las campanas y silbatos que Apple introdujo durante muchos años y, literalmente, volver al desarrollo para iOS 3. Y esto significa:
- Rechazo de AutoLayout (sí, es lento, no lo creas, puedo probarlo en los comentarios)
- Negativa a calcular automáticamente la altura de las celdas de la tabla
Como resultado, se eligió el siguiente concepto:
Vamos a ponernos un poco más detallados.
En el hilo principal, actualizamos la interfaz, procesamos las acciones del usuario (desplácese hacia abajo y tire para actualizar, haga clic en una publicación para aumentarla) y activemos un modelo de servicio para recibir y preparar datos.
Llamada API Aquí todo es simple: NSURLSession con NSURLCache configurado. Es importante que cuando bajemos el historial, usemos el caché, y cuando tire para actualizar, lo deshabilitamos. Exactamente este comportamiento espié en VK y Facebook.
Analizando y creando modelos . Aquí está la lógica para procesar una solicitud específica, arrojar errores y devolver modelos de transporte con datos.
Cálculo de modelos de presentación . El paso más importante para optimizar el rendimiento. Aquí los modelos de transporte se transforman en entidades con el postfix PostModel. ViewModel almacena datos completamente preparados para su visualización: AttributedString, altura de celda calculada (para 2 estados: contraído y expandido), nombre completo como una cadena, cadena de fecha (ya convertida de DateFormatter).
Solo después de eso, los datos vuelven a la transmisión principal. Implementar dicha lógica en Swift es muy conveniente y simple. Hacemos estructura ViewModel. Las estructuras cuando se transfieren a una nueva secuencia se copian.
Excelente concepto listo, ahora hablemos sobre el mecanismo de salida en sí.
Primero tenía que elegir en qué implementar la cinta: UITableView o UICollectionView (definitivamente no habría suficiente tiempo asignado para su implementación). Obviamente, UITableView es adecuado para la salida de la lista, pero estaba muy preocupado si habría problemas para ampliar la lista desde la parte superior, inferior y también para aumentar la celda de contenido. Por lo tanto, decidí pasar de simple a complejo, es decir Si UITableView no tiene problemas, lo dejamos.
Lo primero que decidí fue decidir tirar para actualizar. Para implementar este patrón, hay un UIRefreshControl. Una vez, hace unos 5-6 años, escribí mi implementación usando UIActivityIndicator y cambiando el contentInset de la tabla. Entonces, ¡por favor no lo hagas ahora! UIRefreshControl tiene una interfaz compacta conveniente y toma una nube de muletas, que sin duda tendrá que hacer. Usarlo es muy simple:

Sin embargo, al usarlo, queda claro de dónde provienen los problemas mostrados anteriormente para el cliente VK. Parece que existen en el componente en sí. Rápidamente traté de buscar cuál podría ser la razón y cuáles son las soluciones.
Los consejos de Internet dicen (
tal o
cual ):
- compruebe cuando llame a endRefreshing si el control ahora está actualizado (isRefreshing)
- use exactamente UITableViewController (porque en este caso se llama al método privado mágico)
Intenté esto y aquello, no tuve ningún efecto positivo. Estaba molesto, pero decidí no perder el tiempo y seguir adelante. Por cierto, si alguien sabe cómo superar este problema, escriba los comentarios.
El siguiente paso es implementar la carga de datos desde abajo.
Al principio no me puse muy bien, cuando cargué desde abajo hubo un retraso terrible (aparentemente salté el contenido Tamaño de la tabla):
Y esto es un infierno :-) Con este resultado, definitivamente no puedes ganar. Pero una búsqueda rápida me dio una pista excelente que olvidé:

Y voila:
Queda por decidir cómo animar la altura de la celda.
La primera idea que se me ocurrió fue correr a lo largo de visibleCells y aumentar la altura en el bloque de animación. Pero incluso en la etapa de análisis, esta idea debe descartarse: el problema es que será necesario sincronizar la altura especificada en UITableViewDataSource y hacer algo con contentSize (y Apple lo recomienda mucho).
El segundo pensamiento que se me ocurrió resultó ser correcto: el UITableView tiene métodos de inserción / recarga / eliminación que se pueden ejecutar en el bloque de animación:

No olvides que en heightForRowAt también necesitamos agregar una nueva altura:

¡Todo parece ser, pero no del todo! En la animación del despliegue de la publicación, hubo un punto sutil. Busque el texto en el Intagrama o en Facebook; aparece sin problemas inmediatamente al aumentar la altura. Que hacer ¿Procesarlo línea por línea? Si alcanza el nivel de NSTextContainer, tal vez aparezca una oportunidad similar. Pero me pareció que no era una mala idea (para ese momento) mostrar todo el texto a la vez. Simplemente configure clipToBounds en superView, en el que se ubicará UILabel, mostrando nuestro texto. ¡Y el enfoque funcionó! Oh, esta animación me inspiró mucho y se abrió un segundo viento. Después de todo, no existe tal animación en el cliente nativo de VK. Entonces ella debería agregar oportunidades para ganar :-)
Los detalles restantes en la implementación de la cinta no son tan interesantes. Pero puedes preguntarles en los comentarios. Y no hay nada de malo en diseñar el código. Aquí está:
github.com/katleta3000/vkmobilechallenge . Lamento no haberme peinado y hay números mágicos en algunos lugares (pero este es un concurso de velocidad, tuve que sacrificar algo). Por cierto, puede saltar en confirmaciones allí (los nombres son bastante claros).
El resultado se puede ver aquí:
www.youtube.com/watch?v=Md8YiJxSW1M&feature=youtu.be (la calidad, por supuesto, se pierde mucho, pero es mejor que en el gif para demostrar exactamente un desplazamiento suave)
Estadísticas, resultados, conclusiones
Todo el tiempo el concurso trabajó en un temporizador. Salieron 20 horas y media de tiempo de codificación puro. El tiempo de seguimiento ayudó en 2 cosas: como recordará, había estimaciones en unidades abstractas, por lo que a mediados del último día ya había buenas estadísticas de seguimiento y era posible planificar las tareas finales restantes con mucha más precisión. Y, en segundo lugar, fue posible identificar y probar el patrón de "concentración en una sola sesión". Cada dimensión es una nueva iteración, por lo que resultó que tenía 68 iteraciones de escritura de código. Si promedia, obtendrá 18.5 minutos. Pero, de hecho, en el primer día de iteración, hubo un promedio de 25 minutos, y al final del segundo día, 7 minutos cada uno :-) Comienzas a volverte loco, te pones muy nervioso, te cansas y el rendimiento disminuye. Tales datos ayudarán bien la próxima vez.
Personalmente, utilicé el programa
Hourly (puedes descargarlo y probarlo), simplemente y resuelve la tarea necesaria (e incluso la desarrollo yo mismo):
De las bonificaciones bonitas: para cada rompecabezas cerrado, se muestra una pantalla tranquilizadora muy agradable como esta:
Es curioso que fue cuando se completó la tarea "VK Mobile Challenge" que se me apareció la siguiente pantalla:
Y si! Y así sucedió :-) Procedemos a los resultados.
No tiremos a Peach por la felpa. Peach, que no sabía, es el nombre del gato, el personaje principal de VK. Merch de regalo muy genial:
Tomé el 4to lugar y obtuve 175k rublos. (Sí, y probablemente ya quemaste la imagen en Habra-kat). Y sí, ciertamente estoy satisfecho. Alcancé mi objetivo :-) Y esto es, sin duda, increíblemente agradable.
Al final, me gustaría agradecer a
vkontakte ; después de todo, la competencia fue genial y bien organizada. Y recomiendo a todos los lectores que participen en desafíos y hackatones: esta es una excelente manera de desafiarse a sí mismo y competir con los principales desarrolladores de todo el mundo (bueno, o al menos la comunidad de habla rusa).