Microinteractions dans iOS. Conférence Yandex

Il y a quelques semaines, un événement spécial de la communauté CocoaHeads a eu lieu dans le bureau Yandex - plus grand que les mitaps traditionnels. Le développeur Anton Sergeyev a pris la parole lors de cette réunion et a parlé du modèle de micro-interaction que les concepteurs UX utilisent habituellement, ainsi que de la façon de mettre en pratique les idées qu'il contient. Anton a accordé la plus grande attention à l'animation.


- Il est très important pour moi que j'ai eu l'honneur de rencontrer les invités. Je vois ici ceux que je connais depuis très longtemps, ceux que j'ai connus récemment et ceux que je n'ai pas encore rencontrés. Bienvenue sur CocoaHeads.

Je vais vous parler des micro-interactions. C'est un peu l'appât du clic - nous sommes ingénieurs, développeurs, nous parlerons plus de la partie logicielle, mais nous commencerons par un sujet très humanitaire, comme les micro-interactions. En conséquence, nous appliquerons ce thème humanitaire dans la partie technique pour apprendre à concevoir plus efficacement et simplement de très petits composants visuels, tels que des boutons, de petits chargeurs, des barres. Ils sont saturés d'animation et le code d'animation ramifié peut souvent sembler très compliqué, il est extrêmement difficile à maintenir.

Mais d'abord, un peu de distraction. Pensez-y, vous souvenez-vous quand vous avez décidé de devenir développeur? Je m'en souviens clairement. Tout a commencé avec une table. Une fois, j'ai décidé d'apprendre ObjC. Un langage à la mode, amusant, juste comme ça, sans plans ambitieux. J'ai trouvé un livre, semble-t-il, Big Nerd Ranch, et j'ai commencé à lire chapitre par chapitre, faisant chaque exercice, vérifiant, lisant, jusqu'à ce que j'atteigne la table. Je me suis ensuite familiarisé avec le modèle de délégué, plus précisément avec sa sous-espèce «Data source», source de données. Ce paradigme me semble très simple maintenant: il y a une source de données, délégué, tout est simple. Mais ensuite, cela m'a époustouflé: comment puis-je séparer une table de données complètement différentes? Vous avez déjà vu un tableau sur un morceau de papier dans lequel vous pouvez mettre un nombre infini de lignes, des données complètement abstraites. Cela m'a beaucoup influencé. J'ai réalisé que la programmation, le développement ont d'énormes opportunités, et il sera très intéressant de les appliquer. Depuis lors, j'ai décidé de devenir développeur.

Au cours du développement, divers modèles ont été rencontrés. Énormes, appelées architectures qui décrivent l'ensemble de l'application. Des petits qui tiennent par dizaines dans un petit bouton. Il est important de comprendre que tous ces schémas ne sont pas venus de l'air, mais du secteur humanitaire. Le même modèle de délégué. La délégation est apparue bien avant la programmation, et la programmation reprend toutes ces choses humanitaires pour un travail plus efficace.

Aujourd'hui, je vais parler d'une autre approche qui prend le relais d'une autre chose humanitaire. En particulier, sur les micro-interactions.

Tout a commencé avec un chargeur. Lors des travaux précédents, avant Yandex, j'avais la tâche de répéter le chargeur de conception de matériaux Google. Il y en a deux, l'un indéfini, l'autre défini. J'ai eu la tâche de les combiner en un seul, il devait être à la fois certain et indéfini, mais il y avait des exigences strictes - de sorte que c'était extrêmement lisse. À tout moment, nous pouvons passer d'un état à un autre, et tout doit être animé en douceur et avec précision.

Je suis un développeur intelligent, j'ai tout fait. J'ai reçu plus de 1000 lignes de code de nouilles incompréhensible. Cela a fonctionné, mais j'ai reçu un commentaire sympa sur la révision du code: "J'espère vraiment que personne ne modifiera jamais ce code." Et pour moi, c'est pratiquement inapproprié. J'ai écrit un code affreux. Cela fonctionnait bien, c'était l'une de mes meilleures animations, mais le code était horrible.

Aujourd'hui, je vais essayer de décrire l'approche que j'ai trouvée après avoir quitté ce poste.



Commençons par le sujet le plus humanitaire - les modèles de micro-interaction. Comment sont-ils intégrés et généralement où sont-ils cachés dans nos applications? Continuons à utiliser ce modèle dans notre monde technique. Considérez comment UIView, qui traite de l'affichage et de l'animation, comment cela fonctionne. En particulier, nous parlerons beaucoup du mécanisme CAAction, qui est étroitement intégré à et fonctionne avec UIView, CALayer. Et puis considérez de petits exemples.

Définition d'abord. Apparemment, l'auteur a vraiment aimé le préfixe "micro", mais il n'y a pas de macro ou nano-interactions, la taille n'a pas d'importance. Pour simplifier, nous les appellerons simplement des interactions. Il s'agit d'un modèle tellement pratique qui vous permet de décrire toute interaction avec l'application, du début à la fin. Il se compose de quatre points: un déclencheur, une logique métier qui doit être implémentée dans cette interaction, une rétroaction pour transmettre quelque chose à l'utilisateur et un changement dans l'état de l'application.

Je vais raconter une histoire avec trois rôles différents. Je vais commencer par l'utilisateur, la chose la plus importante dans le développement. Quand je préparais le rapport, je suis tombé malade. J'avais besoin de trouver une pharmacie et j'ai ouvert Yandex.Map. J'ai ouvert l'application, regardez-la, elle me regarde, mais rien ne se passe. J'ai alors réalisé que j'étais l'utilisateur, je suis le principal, je donne des instructions sur ce qu'il faut faire pour l'application. J'ai orienté, cliqué sur le bouton «rechercher», entré «pharmacie», cliqué sur «OK», l'application a fait le travail interne, trouvé les pharmacies nécessaires à côté de moi et l'affichait sur l'écran.

J'ai cherché la bonne et j'ai constaté qu'en plus des pharmacies, un bouton spécial apparaissait à l'écran - pour construire un itinéraire. Ainsi, l'application est passée à un nouvel état. J'ai cliqué dessus et je suis allé à la pharmacie. Je suis entré dans cette application pour une raison quelconque - pour trouver une pharmacie. Je l'ai atteinte. Je suis un utilisateur satisfait.

Avant que cette application n'apparaisse et que j'aie pu y chercher quelque chose, elle a d'abord été développée. Qu'a pensé le concepteur UX lorsqu'il a mis au point ce processus? Tout a commencé par le fait qu'il fallait sortir d'une scène muette lorsque l'utilisateur et l'application se regardent, et rien ne se passe. Pour cela, une sorte de déclencheur était nécessaire. Tout a un début, et là aussi, il fallait commencer quelque part.

Le déclencheur a été sélectionné - bouton de recherche. En cliquant dessus, il fallait résoudre le problème d'un point de vue technique. Demander des données sur le serveur, analyser la réponse, mettre à jour le modèle, analyser. Demandez la position actuelle des utilisateurs et plus encore. Nous avons donc obtenu ces données et nous savons exactement où se trouvent toutes les pharmacies.

Il semblerait qu'il soit possible d'y mettre fin. Après tout, nous avons résolu le problème, trouvé toutes les pharmacies. Il n'y a qu'un seul problème: l'utilisateur ne sait toujours rien de ces pharmacies. Il a besoin de le faire passer.

D'une manière ou d'une autre, emballer notre solution à ce problème et la lui apporter dans un bel emballage pour qu'il le comprenne. Il se trouve que les utilisateurs sont des personnes, ils interagissent avec le monde extérieur à travers les sens. L'état actuel de la technologie est tel que nous, en tant que développeurs d'applications mobiles, n'avons que trois sens: la vision - nous pouvons montrer quelque chose sur l'écran, l'ouïe - nous pouvons reproduire dans les haut-parleurs, et une sensation tactile, nous pouvons pousser l'utilisateur dans la main.

Mais l'homme est beaucoup plus fonctionnel. Mais l'état actuel de la technologie est tel que pour le moment nous ne pouvons compter que sur ces trois-là. Et dans ce cas, nous sélectionnons un écran, affichons sur la carte les pharmacies les plus proches et une liste avec des informations plus détaillées sur ces pharmacies. Et il semblerait que c'est exactement tout, l'utilisateur a trouvé des pharmacies et tout va bien.

Mais il y a un problème. Lorsque l'utilisateur est entré dans l'application, il se trouvait dans un contexte dans lequel il ne savait pas où se trouvaient les pharmacies. Et ses tâches consistaient à la retrouver. Mais maintenant le contexte a changé, il sait où sont les pharmacies, il n'a plus besoin de les chercher. Il avait la tâche suivante - obtenir des instructions pour la prochaine pharmacie. C'est pourquoi nous devons afficher des contrôles supplémentaires à l'écran, en particulier, c'est un bouton pour construire un itinéraire, c'est-à-dire transférer l'application dans un autre état dans lequel elle est prête à accepter à nouveau de nouveaux déclencheurs pour les prochaines interactions.

Imaginez, le concepteur UX a proposé tout cela, vient au développeur et commence à décrire en couleurs comment l'utilisateur clique sur le bouton, comment et ce qui se passe, comment il est recherché, comment l'utilisateur est satisfait, comment nous augmentons la DAU et ainsi de suite. La pile de questions non résolues du développeur a débordé ailleurs sur la première phrase, lorsque nous avons mentionné le bouton pour la première fois.

Il écoute patiemment tout, et à la fin, quand ça se termine, il dit que d'accord, c'est cool, mais parlons du bouton. C'est un élément important.

Au cours de la discussion, il s'avère que le bouton est intrinsèquement un déclencheur, il contient en lui-même une logique par laquelle il peut recevoir des messages du système, en particulier, sur les clics des utilisateurs sur l'écran. Sur la base de ce clic, il peut démarrer une chaîne d'événements, qui commence par le fait que le même bouton envoie des messages à différents objets sur la nécessité de démarrer divers processus, dans ce cas, demander des informations sur le serveur, un peu plus.

Lorsqu'il est enfoncé, le bouton change d'état, il devient enfoncé. Lorsque l'utilisateur relâche, il cesse d'être pressé. Autrement dit, il donne un retour à l'utilisateur afin qu'il comprenne à quoi s'attendre de ce bouton. Et le bouton peut être pressé, non pressé, actif ou inactif, dans différents états, et se déplacer selon une logique différente d'un état à l'autre.

Ainsi, nous avons vu que le même modèle de micro-interaction, qui consiste en un déclencheur, une logique métier, un retour d'expérience et des changements d'état, peut décrire notre application à différentes échelles, comme à l'échelle d'un cas utilisateur complet, une énorme recherche de la pharmacie la plus proche, donc en termes de petit bouton.

Et c'est un modèle très pratique qui vous permet de simplifier l'interaction au sein de l'équipe et de décrire par programme, quatre entités distinctes: déclencheur, logique métier, rétroaction et changement d'état. Voyons ce que UIKit nous offre pour l'utiliser. Et non seulement fournit, mais utilise. Lors de la mise en œuvre de diverses animations, les petits composants des sous-classes UIView, il utilise uniquement ce mécanisme, et ne va pas de manière différente.

Commençons par UIView, comment il s'intègre dans ce modèle. Ensuite, nous considérerons CALayer qu'il nous fournit pour soutenir ces états, et nous considérerons le mécanisme des actions, le moment le plus intéressant.

Commençons par l'UIView. Nous l'utilisons pour afficher des rectangles à l'écran. Mais en fait, UIView ne sait pas se dessiner, il utilise pour cela un autre objet CALayer. En fait, UIView s'engage à recevoir des messages sur le toucher du système, ainsi que sur d'autres appels, concernant l'API que nous avons définie dans nos sous-classes d'UIView. Ainsi, UIView implémente lui-même la logique de déclenchement, c'est-à-dire le lancement de certains processus, recevant ces messages du système.

UIView peut également informer ses délégués des événements qui se sont produits et également envoyer des messages aux abonnés, comme, par exemple, faire des sous-classes UIControl des événements différents. De cette façon, la logique métier de cette UIView est implémentée. Tous n'ont pas de logique métier, beaucoup d'entre eux ne sont que des éléments d'affichage et n'ont pas de rétroaction au sens de la logique métier.



Nous avons examiné deux points, un déclencheur et une logique métier. Et où les commentaires et les changements d'état sont-ils cachés dans UIView? Pour comprendre cela, nous devons nous rappeler que UIView n'existe pas par lui-même. Une fois créé, il crée un backlayer, une sous-classe de CALayer.



Et se désigne lui-même son délégué. Pour comprendre comment UIView utilise CALayer, il peut exister dans différents états.

Comment distinguer un état d'un autre? Ils diffèrent par l'ensemble de données qui doit être stocké quelque part. Nous considérerons quelles fonctionnalités CALayer nous fournit pour UIView, afin qu'il stocke l'état.



Notre interface se développe un peu, l'interaction entre UIView et CALayer, UIView a une tâche supplémentaire - mettre à jour le référentiel dans CALayer.

Un fait peu connu que peu de gens utilisent: CALayer peut se comporter comme un tableau associatif, ce qui signifie que nous pouvons y écrire des données arbitraires sur n'importe quelle clé comme suit: setValue (_: forKey :).



Cette méthode est présente dans toutes les sous-classes NSObject, mais contrairement à beaucoup d'autres, lorsqu'une clé est reçue qui n'est pas remplacée par elle, elle ne se bloque pas. Et il l'écrit correctement, puis nous pouvons le lire. C'est une chose très pratique qui permet, sans créer de sous-classes de CALayer, d'y écrire des données puis de les lire, en les consultant. Mais il s'agit d'un référentiel simple très primitif, en fait, un dictionnaire. CALayer est beaucoup plus progressif. Il prend en charge les styles.

Ceci est implémenté par la propriété Style que possède n'importe quel CALayer. Par défaut, il est nul, mais nous pouvons le redéfinir et l'utiliser.



En général, c'est un dictionnaire ordinaire et rien de plus, mais il a une particularité sur la façon dont CALayer fonctionne avec lui, si nous demandons la valeur de Key, une autre méthode que NSObject a. Il agit de manière très intéressante, il recherche récursivement les valeurs nécessaires dans le dictionnaire de style. Si nous emballons un style existant dans un nouveau style avec la clé de style et y écrivons quelques clés, mais cela se présentera de la manière suivante.



Regardez d'abord la racine, puis l'intérieur des terres et ainsi de suite, jusqu'à ce que cela ait du sens. Lorsque le style devient nul, cela n'a aucun sens de chercher plus loin.

De cette façon, UIView peut, en utilisant l'infrastructure fournie par CALayer, organiser les changements d'état, mettre à jour le référentiel CALayer interne, soit en utilisant le style, un référentiel très puissant qui peut simuler une pile, soit en utilisant un tableau associatif régulier, qui est également très efficace et très utile .

Fini le stockage, commencez par CAAction. Je vais vous en dire plus sur lui.



Il y a une nouvelle tâche pour UIView - demander des actions à CALayer. Quelles sont les actions?



CAAction est juste un protocole qui n'a qu'une seule méthode - exécuter. Apple aime généralement les thèmes du cinéma, l'action ici comme «appareil photo, moteur!». Ce «moteur» n'est qu'une action, et pas seulement ce nom a été utilisé. La méthode d'exécution signifie démarrer une action qui peut démarrer, exécuter et terminer, ce qui est le plus important. Cette méthode est très générique, elle n'a qu'une chaîne d'événements et tout le reste peut être de n'importe quel type. En ObjC, tout cela est id et un NSDictionary normal.



Il existe des classes dans UIKit qui satisfont au protocole CAAction. Le premier est l'animation. Tout d'abord, nous savons que l'animation peut être ajoutée à un calque, mais c'est une chose de très bas niveau. Une abstraction de haut niveau au-dessus consiste à exécuter une action avec les paramètres nécessaires avec une couche.

La deuxième exception importante est NSNull. Nous savons qu'il ne peut pas être appelé par aucune méthode, mais il satisfait le protocole CAAction, et cela est fait afin de rechercher commodément CAAction dans les couches.



Comme nous l'avons dit précédemment, UIView est un délégué à CALayer, et l'une des méthodes de délégué est l'action (pour: forKey :). La couche a une méthode, action forKey.



Nous pouvons l'appeler à tout moment sur le calque, et à tout moment il donnera l'action correcte ou nulle, car il peut également donner. L'algorithme est une recherche très inhabituelle. Le pseudocode est écrit ici, regardons les lignes. Dès réception d'un tel message, il consulte d'abord le délégué. Un délégué peut soit retourner nil, ce qui signifie que la recherche doit se poursuivre ailleurs, soit renvoyer une action valide, un objet valide qui satisfait le protocole CAAction. Mais il y a une règle logique: s'il renvoie NSNull, qui satisfait ce protocole, il sera ensuite converti en nil. Autrement dit, si nous renvoyons Null, cela signifie en fait «arrêtez de chercher». Il n'y a aucune action et ce n'est pas nécessaire.

Mais il y a ce qui suit. Après avoir consulté le délégué et que le délégué est revenu à zéro, il a poursuivi ses recherches. Tout d'abord, dans le dictionnaire Actions, que possède le calque, puis il recherchera récursivement dans le dictionnaire de style, où il peut également y avoir un dictionnaire avec la clé d'actions, dans lequel de nombreuses actions peuvent être écrites, et il pourra également les rechercher récursivement. Si cela n'a pas fonctionné, il demandera à la classe l'action par défaut pour la méthode Key, qui est définie par CALayer et jusqu'à récemment renvoyé quelque chose, mais dernièrement, elle retourne toujours zéro dans les dernières versions d'iOS.

Compris la théorie. Voyons comment tout est appliqué dans la pratique.

Il y a des événements, ils ont des clés, certaines actions ont lieu sur ces événements. Fondamentalement, deux types d'événements différents peuvent être distingués. Le premier est l'animation des propriétés stockées. Supposons que lorsque nous appelons Viewcolor = red sur View, il est théoriquement possible d'animer.



Quel rapport sur les modèles sans circuit? J'en ai dessiné deux. UIView a une sorte d'interface que nous avons définie pour les sous-classes ou celle qui est reçue du système avec des événements. La tâche de UIView est de demander l'action souhaitée, de mettre à jour le magasin interne et de lancer l'action qui s'est produite. L'ordre est très important par rapport à la demande: action, seulement ensuite mettre à jour l'action, et seulement ensuite mettre à jour le magasin et l'action.



Que se passe-t-il si à UIView nous mettons à jour backgroundColor. Nous savons que dans UIView, tout ce qui concerne l'affichage à l'écran est toujours un proxy pour CALayer. Il met en cache tout ce qu'il reçoit, juste au cas où, mais en même temps tout diffuse CALayer, et il traite de toute la logique plus loin sur CALayer. Que se passe-t-il dans CALayer lorsqu'il reçoit une tâche pour changer l'arrière-plan? Tout est un peu plus compliqué ici.



Pour commencer, il demandera une action. Et il est important de comprendre que l'action sera demandée en premier. Cela vous permettra de demander à CALayer ses valeurs actuelles, y compris backgroundColor, au moment de la création de l'action, seulement alors le magasin sera mis à jour, et lorsque l'action reçue recevra la commande d'exécution, il pourra consulter CALayer et obtenir de nouvelles valeurs. Ainsi, il possédera à la fois l'ancien et le nouveau, ce qui lui permettra de créer des animations, si nécessaire.

Mais il y a une fonctionnalité dans UIView, si nous changeons le backgroundColor dans UIView, si nous le faisons dans le bloc d'animation, alors il est animé, et s'il est en dehors du bloc d'animation, il n'est pas animé.



Tout est très simple, il n'y a pas de magie. Mais rappelez-vous simplement que UIView est un délégué de CALayer, il a une telle méthode. Tout est très simple.

Si cette méthode a été exécutée dans le bloc d'animation, elle renverra une sorte d'action. Si en dehors du bloc d'animation, cette méthode retournera NSNull, ce qui signifie que rien n'a besoin d'être animé. , CALayer .

, UIView , . . ?



, . UIView , read only, , inheritedAnimationDuration. . , . .

? duration, . , run, , .



, CAAction, backgroundcolor opacity, UIView . , , , , . . setValue forKey , , , , , , .

, , , , .

— . , «» «» . .

.



. , , , . UIView CALayer, , , CAAction, , , .





, , , . , . .

. - .

CAAction, , . , , , .



, , , home, . , . , .



. - .



, - , - . , , . , .

, , .



, CAAction , . , UIControl, - , - , , , - .

, . , UIView -, , - , , , .

— . .

? . , . — . activating, inactive active. , , .



. , onOrderIn onOrderOut. , UIKit, .

, -, — , .



. UIView , : isActive progress. . CAAction, .

, . , , 30 CAACtion, . , 30 , NSNull. 15 15 . . — . , — .

, . .

. , , : , -, .

En décomposant toutes les interactions en ces quatre morceaux, on ne peut pas interférer avec la logique de l'un avec l'autre, simplifier. Par conséquent, essayez d'analyser vos applications et diverses tâches en utilisant ces micro-interactions. N'oubliez pas qu'UIKit fournit une énorme infrastructure pour le faire facilement et magnifiquement. Ne la négligez pas. Il existe souvent des méthodes très anciennes, elles sont rarement utilisées, mais elles sont très importantes et vous aideront à réaliser vos éléments magnifiquement, simplement et rapidement. Merci de votre attention.

Source: https://habr.com/ru/post/fr429580/


All Articles