Bonjour à tous! Récemment, il y a eu un concours du VKontakte Mobile Challenge, et mon travail a remporté un prix. Sur les instructions de la deuxième étape, il a été nécessaire de développer un fil d'actualités pour les appareils mobiles, et les principaux critères d'évaluation étaient le défilement fluide et le post-chargement. Lorsque j’ai participé, j’ai décidé que quel que soit le résultat final, j’essaierai d’écrire un article sur l’approche de la mise en œuvre de la bande et sur mes émotions et sentiments pendant le concours. Ce que j'ai fait. Sous le chat, des trucs et astuces pour développer des flux d'actualités en mode narration.

À propos du concours
Tout d'abord, il convient de parler un peu de la concurrence. Comme je le sais, VKontakte organise chaque année de tels événements pour les développeurs mobiles. J'ai moi-même déjà participé en 2012 et 2013. Les tâches consistaient respectivement à développer des filtres de chat et d'image. En 2013, j'ai réussi à me qualifier pour le tour final et à gagner 100 000 roubles, ce qui m'a alors paru un très bon montant pour 4 jours de repos sur un travail intéressant.
Et, en voyant les publicités sur un réseau social, j'ai décidé que je devais essayer, parce que le truc cool est de vous prouver que vous êtes toujours capable d'écrire du code de haute qualité en très peu de temps).
Il y avait 2 étapes dans la compétition: dans la première, il était proposé de passer un test de 30 questions, de résoudre 2 problèmes d'Olympiade et un de haute qualité (c'est-à-dire qu'une solution est un ensemble de vos pensées sous forme de texte). Dans le deuxième tour de 1000+ personnes, seulement 112 ont réussi (nous ne parlons que d'iOS, sur Android des valeurs légèrement inférieures) et la scène principale a commencé - le développement d'applications.
Au moment de l'affectation, vous deviez créer un flux d'actualités, écrire vous-même l'intégration avec l'API VK, le flux aurait dû être très fluide sur tous les appareils, il y avait certaines exigences pour la sortie de contenu (publications avec images et carrousel, compteurs de likes, partages, nombre de vues, ainsi que la possibilité de minimiser et déployer le poste sous certaines conditions).
Les dispositions ont été présentées dans Figma. C'est un bon choix pour la compétition - en raison du nombre d'utilisateurs actifs, la concurrence est immédiatement ressentie (et même éliminée samedi, car la limite simultanée de 50 connexions a été dépassée).
D'une manière générale, l'organisation du concours s'est déroulée sans heurts sauf quelques points: immédiatement après la publication de la mission, le lien vers les mises en page s'est avéré rompu, puis, comme je l'ai dit plus haut, samedi l'entrée de Figma a été bloquée (ce problème a été résolu très rapidement), enfin, après l'événement, cela a pris un bon moment attendre des résultats. Mais tous les négatifs atténuent un article similaire:
Plan et stratégie de victoire
Un bloc très important et intéressant si vous participez à des compétitions ou souhaitez essayer. Avant de participer, je recommande à tout le monde de réfléchir à quel objectif vous poursuivez et quel résultat vous souhaitez obtenir en sortant. Avant de commencer le concours, j'ai décidé moi-même que je voulais le gagner (c'est-à-dire prendre le prix). Et pour ce faire, vous avez besoin d'une stratégie:
Voici la formule d'une stratégie gagnante (elle me semble universelle et simple, mais je suis sûr que la plupart des participants ne la suivent pas):
- Lisez attentivement le devoir . Après quelques heures, relisez-le (je me suis retrouvé plusieurs fois dans une situation où je n'ai pas perçu de tâches la première fois). Et quelques heures avant la livraison une fois de plus au cas où.
- Formulez des questions et posez-les aux organisateurs (généralement les exigences de la mission ne prennent pas en compte de nombreuses situations, ou sont exposées très superficiellement). C'est exactement ce que j'ai fait, en obtenant des réponses dans le style de "votre choix".
- Faites un plan de projet. Oui, la participation à une compétition ou un hackathon est un projet. Le projet a un objectif, un calendrier, des ressources et un budget. Le temps est strictement limité et il est impossible de déplacer la fin du changement. Aucune personne supplémentaire ne peut être invitée - la compétition n'est pas une compétition par équipe. Tout ce que vous avez, c'est votre propre peuple. / heures. Déterminez immédiatement combien vous êtes prêt à dépenser (pensez bien combien cela ira au sommeil, à la nourriture, à la castration, aux pauses, et oui, votre productivité chutera en raison de la fatigue et des nerfs d'une sensation d'impuissance devant un laps de temps inexorablement proche).
- Le plan doit avoir des tâches, leurs priorités et leurs pondérations (j'ai utilisé des estimations de la forme 1, 2, 4, 8). L'évaluation comprend la durée, la réticence à l'accomplir et les risques potentiels (complexité, conditions incompréhensibles, aucune expérience dans un tel développement, etc.). Ensuite, vous établissez un plan détaillé (ou des tâches de feuille de route) - d'abord le plus important et le plus complexe (laissez-le facile et compréhensible à la fin).
- Choisissez quelques grands intervalles ou jalons (les jours sont excellents - nous avions 3 jours). Et au fur et à mesure que vous les parcourez, examinez votre plan de travail, décomposez les tâches, triez-les et, surtout, rayez celles que vous avez déjà faites avec plaisir.
Maintenant, le conseil (je l'ai fait juste): regardez attentivement comment la tâche est formulée, si elle contient les mots «requis» et «en plus». Et quels sont les critères d'évaluation pour les éléments du "supplémentaire", c'est-à-dire s'ils passeront outre à leur manque d'exigences mises en œuvre dans la section «obligatoire». J'en doute. Par conséquent, concentrez-vous sur la section «obligatoire». Pour ma part, j'ai généralement délibérément biffé la section «avancée» et je n'avais même pas l'intention de la démarrer. - Lors de la mise en œuvre, faites attention aux détails . Par exemple, si la tâche comporte de nombreux formulaires d'interface, faites-les de la manière dont ils sont affichés sur les présentations. Faites attention aux retraits, polices, ombres, interligne, etc. Cela vous ajoutera certainement des points pour la précision et la cohérence. Soit dit en passant, dans Figma, contrairement à Zeplin, il existe une très large exportation de paramètres, par exemple, vous pouvez obtenir une bonne NSAttributedString.
La mission a été ouverte dans la nuit de jeudi à vendredi. Le vendredi est une journée de travail et il n'y avait ni opportunité ni envie de participer à un concours au travail. Par conséquent, après mon retour du travail le vendredi soir, je me suis d'abord engagé dans la planification, l'évaluation et le calcul du temps réel que je peux passer.
Le plan initial était le suivant:
Jour | Le temps | Les tâches |
Vendredi | 4 heures | Autorisation, obtention des données pour le profil et la bande. |
Samedi | 9 heures | Le prototype de la bande avec l'affichage de carrés rouges de différentes hauteurs, en les chargeant par dessus pour les actualiser et en les chargeant à l'infini dans l'historique, en préparant tous les modèles et services (en travaillant avec l'API, la mise en cache des demandes, la mise en cache des images).
|
Dimanche | 9 heures | Sortie de données réelles (texte, images individuelles et carrousels), compteurs de likes, vues et réglage final par mises en page. |
Technique de mise en œuvre des bandes
Hourra! Messieurs, développeurs, vous avez trouvé le bon bloc. Ici, nous parlerons de la mise en œuvre de la partie principale de la tâche - le fil d'actualité.
En général, si nous nous désengageons, le fil de nouvelles n'est qu'une implémentation appliquée, en général, nous parlerons de la création d'une liste d'entités hétérogènes (c'est-à-dire que chacune peut avoir son propre affichage, ce qui nécessite des calculs supplémentaires et sa propre hauteur, qui peut changer dynamiquement même après l'affichage du message) , ainsi que la liste peut être mise à jour via pull-to-refresh et charger à l'infini les données ci-dessous.
C'est le problème général que j'ai décidé de résoudre en premier lieu. La première étape consiste à analyser les solutions existantes. Vous devez vous concentrer sur les plus forts, j'ai donc choisi 3 applications à comparer: Vkontakte, Facebook, Instagram.
J'ai donc voulu mener une étude sur les 6 problèmes les plus aigus et critiques:
- Pull-to-refresh (ajouter en haut de la liste)
- Chargement de l'historique fluide (ajouter à la fin de la liste)
- Défilement rapide (en alternance avec les doigts, nous accélérons la bande autant que la force de friction le permet)
- Faites défiler vers le haut (accumulez une grande histoire et cliquez sur la barre d'état)
- Y a-t-il une divulgation (augmentation) dynamique du message et quelle est l'animation
- Fonctionnement de la bande en mode de perte de paquets presque complète (Développeur -> Conditionneur de liaison réseau -> Très mauvais réseau)

En général, toutes les applications se comportent bien, mais j'ai quand même remarqué quelques problèmes.
Voir, par exemple, comment se comporte le pull-to-refresh sur VKontakte si vous ne relâchez pas votre doigt pendant la mise à jour et tirez doucement le ruban vers le haut (voir le gif à gauche).
Voyez-vous également ce saut?
Instagram et Facebook n'ont pas montré un tel comportement.
Et il y a aussi une différence notable dans la divulgation postérieure. Pour Facebook et Instagram, cela se produit avec une animation fluide, et VKontakte met simplement à jour la taille en cliquant.



Notre tâche est donc de faire un défilement fluide, de télécharger des articles et de les révéler avec une animation.
La première étape consiste à choisir un concept et à créer un prototype sur des carrés rouges (je me demande pourquoi je choisis toujours intuitivement une couleur rouge pour les prototypes. Est-ce le cas pour tout le monde?).
Mon idée principale en améliorant la productivité était d'abandonner toutes les cloches et les sifflets qu'Apple a introduits pendant de nombreuses années et de revenir littéralement au développement pour iOS 3. Et cela signifie:
- Refus d'AutoLayout (oui, c'est lent, ne le croyez pas - je peux le prouver dans les commentaires)
- Refus de calculer automatiquement la hauteur des cellules du tableau
En conséquence, le concept suivant a été choisi:
Obtenons un peu plus de détails.
Dans le fil principal, nous mettons à jour l'interface, traitons les actions des utilisateurs (faites défiler vers le bas et tirez pour actualiser, cliquez sur une publication pour l'agrandir) et déclenchons un modèle de service pour recevoir et préparer les données.
Appel API . Tout est simple ici - NSURLSession avec NSURLCache configuré. Il est important que lors du chargement de l'historique, nous utilisions le cache et lorsque le pull-to-refresh soit désactivé. Exactement ce comportement que j'ai espionné sur VK et Facebook.
Analyse et création de modèles . Voici la logique pour traiter une demande spécifique, générer des erreurs et renvoyer des modèles de transport avec des données.
Calcul des modèles de présentation . L'étape la plus importante pour optimiser les performances. Ici, les modèles de transport sont transformés en entités avec le suffixe PostModel. ViewModel stocke des données entièrement préparées pour l'affichage - AttributedString, hauteur de cellule calculée (pour 2 états: réduit et développé), nom complet sous forme de chaîne, chaîne de date (déjà convertie à partir de DateFormatter).
Ce n'est qu'après cela que les données reviennent au flux principal. L'implémentation d'une telle logique sur Swift est très pratique et simple. Nous créons la structure ViewModel. Les structures lors de leur transfert vers un nouveau flux sont copiées.
Excellent concept prêt, parlons maintenant du mécanisme de sortie lui-même.
Vous devez d'abord choisir sur quoi implémenter la bande - UITableView ou UICollectionView (il n'y aurait certainement pas assez de temps alloué pour votre implémentation). Évidemment, UITableView convient pour lister la sortie, mais j'étais très inquiet s'il y aurait des problèmes avec l'agrandissement de la liste du haut, du bas et l'augmentation de la cellule de contenu. J'ai donc décidé de passer du simple au complexe - c'est-à-dire si UITableView n'a aucun problème, nous le laissons.
La première chose que j'ai décidé a été de décider du pull-to-refresh. Pour implémenter ce modèle, il existe un UIRefreshControl. Il y a environ 5 à 6 ans, j'ai écrit mon implémentation à l'aide de UIActivityIndicator et en changeant le contentInset de la table. Alors, ne le faites pas maintenant! UIRefreshControl a une interface compacte pratique et prend un nuage de béquilles, que vous devrez certainement faire. Son utilisation est très simple:

Cependant, lors de son utilisation, il devient clair d'où proviennent les problèmes indiqués ci-dessus pour le client VK. Il semble qu'ils existent dans le composant lui-même. J'ai rapidement essayé de chercher quelle pourrait être la raison et quelles sont les solutions.
Les conseils Internet disent (
tel ou
tel ):
- vérifier lors de l'appel de endRefreshing si le contrôle est maintenant mis à jour (isRefreshing)
- utiliser exactement UITableViewController (car dans ce cas, la méthode magique privée est appelée)
J'ai essayé ceci et cela - je n'ai eu aucun effet positif. J'étais bouleversé, mais j'ai décidé de ne pas perdre de temps et de continuer. Soit dit en passant, si quelqu'un sait comment surmonter ce problème - veuillez écrire dans les commentaires.
L'étape suivante consiste à implémenter le chargement des données par le bas.
Au début, je n'étais pas très cool - lors du chargement par le bas, il y avait un terrible décalage (j'ai sauté le contentSize du tableau apparemment):
Et c'est l'enfer :-) Avec ce résultat, vous ne pouvez certainement pas gagner. Mais une recherche rapide m'a donné un indice formidable que j'ai oublié:

Et le tour est joué:
Reste à décider comment animer la hauteur de la cellule.
La première idée qui m'est venue à l'esprit était de courir le long de visibleCells et d'augmenter la hauteur dans le bloc d'animation. Mais même au stade de l'analyse, cette idée doit être rejetée - le problème est qu'il sera nécessaire de synchroniser la hauteur spécifiée dans UITableViewDataSource et de faire quelque chose avec contentSize (et cela est fortement recommandé par Apple).
La deuxième pensée qui m'est venue à l'esprit s'est avérée correcte - l'UITableView a des méthodes d'insertion / rechargement / suppression qui peuvent être exécutées dans le bloc d'animation:

N'oubliez pas que dans heightForRowAt, nous devons également ajouter une nouvelle hauteur:

Tout semble être, mais pas tout à fait! Dans l'animation du déploiement du poste, il y avait un point subtil. Recherchez le texte dans l'Intagram ou Facebook - il apparaît immédiatement en douceur avec une hauteur croissante. Que faire Le rendre ligne par ligne? Si vous atteignez le niveau de NSTextContainer, alors peut-être une opportunité similaire apparaîtra. Mais il me semblait que ce n'était pas une si mauvaise idée (pour une telle période) d'afficher tout le texte à la fois. Définissez simplement clipToBounds sur superView, dans lequel UILabel sera situé, affichant notre texte. Et l'approche a fonctionné! Oh, cette animation m'a beaucoup inspiré et un second souffle s'est ouvert. Après tout, il n'y a pas une telle animation dans le client natif de VK. Elle devrait donc ajouter des chances de gagner :-)
Les détails restants dans la mise en œuvre de la bande ne sont pas si intéressants. Mais vous pouvez leur demander dans les commentaires. Et il n'y a rien de mal à présenter le code. Le voici -
github.com/katleta3000/vkmobilechallenge . Je suis désolé de ne pas être peigné et il y a des numéros magiques à certains endroits (mais c'est un concours de vitesse, j'ai dû sacrifier quelque chose). Au fait, vous pouvez sauter sur les commits là-bas (les noms sont assez clairs).
Le résultat peut être vu ici -
www.youtube.com/watch?v=Md8YiJxSW1M&feature=youtu.be (la qualité est bien sûr mal perdue, mais meilleure que dans le gif pour démontrer un défilement parfaitement fluide)
Statistiques, résultats, conclusions
Tout le temps, le concours a fonctionné sur une minuterie. 20 heures et demie de temps de codage pur sont sorties. Le temps de suivi a aidé à 2 choses: comme vous vous en souvenez, il y avait des estimations en unités abstraites, donc au milieu de la dernière journée, il y avait déjà de bonnes statistiques de suivi et il était possible de planifier les tâches finales restantes de manière beaucoup plus précise. Et, deuxièmement, il a été possible d'identifier et de prouver le schéma de «concentration en une seule séance». Chaque dimension est une nouvelle itération, donc il s'est avéré que j'avais 68 itérations d'écriture de code. Si vous faites une moyenne, vous obtiendrez 18,5 minutes. Mais en fait, le premier jour de l'itération, il y avait en moyenne 25 minutes, et à la fin du deuxième jour, 7 minutes chacune :-) Vous commencez à devenir fou, à devenir très nerveux, à vous fatiguer et les performances diminuent. Ces données seront utiles la prochaine fois.
Personnellement, j'ai utilisé le programme
Hourly (vous pouvez le télécharger et l'essayer) - il résout simplement et résout la tâche nécessaire (et même le développe moi-même):
Parmi les bonus sympas - pour chaque puzzle fermé, vous voyez un très bel écran rassurant comme ceux-ci:
C'est drôle que ce soit à la fin de la tâche «VK Mobile Challenge» que l'écran suivant m'est apparu:
Et oui! Et c'est arrivé :-) Nous procédons aux résultats.
Ne tirons pas Peach pour la peluche. Peach, qui ne savait pas, est le nom du chat - le personnage principal de VK. Merch cadeau très cool:
J'ai pris la 4e place et j'ai obtenu 175 000 roubles. (Oui, et vous avez probablement déjà brûlé l'image sur Habra-kat). Et oui, je suis certainement content. J'ai atteint mon objectif :-) Et c'est sans aucun doute incroyablement agréable.
Au final, je voudrais dire merci à
vkontakte - après tout, la compétition était cool et bien organisée. Et je recommande à tous les lecteurs de participer aux défis et aux hackathons - c'est un excellent moyen de vous mettre au défi et de rivaliser avec les meilleurs développeurs du monde (enfin, ou du moins la communauté russophone).