Chainer élégamment les UIViewPropertyAnimators

Thibault Wittemberg
Nuglif
Published in
4 min readApr 9, 2018

Vous trouverez la version anglaise de cet article sur mon blog personnel: twittemb.github.io.

Habituellement mes articles sont plutôt orientés sur les Design Patterns ou les architectures logicielles, mais celui-ci est légèrement différent, et je ne m’attendais franchement pas à écrire sur ce type de sujet.

Malgré tout, je pense avoir quelque chose de cool à partager : aujourd’hui, nous allons parler des animations en Swift.

Je ne suis pas un expert en UX/UI, donc nous n’allons pas détailler les subtilités des frameworks d’animation sous iOS, mais dernièrement j’ai eu un défis à relever ici chez Nuglif.

Un collègue est arrivé avec ce problème : « Dans notre application, nous avons plusieurs animations qui sont jouées en séquence et la façon dont nous le faisons peut être améliorée. Est-ce qu’il existerait d’après vous une manière élégante de le faire ? »

UIView.animate()

Les optimisations auxquelles il faisait référence sont induites par la façon traditionnelle dont nous chainons les animations avec UIKit.

Considérons cette séquence :

Avant iOS 10, nous aurions coder celle-ci de cette manière :

UIView.animate() fournit un bloc de complétion que nous utilisons pour déclencher l’animation suivante. De cette façon, nous pouvons créer une chaine d’animations. Comme vous pouvez le constater, le principal inconvénient de cette technique est la lourdeur du code. C’est à peine lisible.

UIViewProperlyAnimator

Heureusement, iOS10 a introduit UIViewProperlyAnimator. Je ne vais pas décrire toutes les fonctionnalités du framework, une introduction intéressante en est faite ici: useyourloaf.

Ce qui m’importe est comment améliorer la lisibilité du mécanisme de chainage.

Premièrement, UIViewPropertyAnimator améliore nativement la technique de complétion.

Le block de code précédent devient donc :

Cela permet de séparer clairement la définition de chaque animation de la manière dont elles sont connectées ensemble.

Non seulement nous avons une meilleur lisibilité du code mais il est aussi mieux découpé. Nous pouvons imaginer chainer différentes animations en fonction du contexte. Plutôt cool.

Mais, nous pouvons aller plus loin …

Enchainement réactif d’animations

Je suis un grand fan de la programmation Reactive et la première chose qui m’est venu à l’esprit était : “Existe -t-il un moyen de rendre UIViewPropertyAnimator compatible avec RxSwift ?

De quoi voudrions nous être notifié avec la programmation Reactive ? C’est plutôt évident : de la fin d’une animation, afin de pouvoir déclencher la suivante.

Traditionnellement, rendre un type compatible avec RxSwift, consiste à ajouter une extension conditionnelle à la structure “Reactive”. C’est un bon point de départ.

Nous devons maintenant déterminer plus précisement ce que devra faire cette extension, et ce qu’elle devra retourner ?

Quelque part, elle devra encapsuler les appels à startAnimation() et à addCompletion() de UIViewPropertyAnimator. Elle devra également retourner un Observable. Pour des questions de simplicité et d’efficacité, nous allons considérer qu’une animation peut seulement “se compléter”. Il n’y aura pas de gestion exhaustive d’un flux Rx (pas de onNext, onSubscribed, onDisposed, etc). Cette hypothèse a une conséquence très favorable sur notre implémentation. Notre extension Reactive ne retournera pas un Observable mais simplement un Completable. Un Completable peut seulement être terminé ou en erreur.

Sans plus attendre, voici cette extension:

La “computed property” animate retourne un Completable, qui lorsque quelqu'un y souscrit, démarre l’animation et lui ajoute un block de completion qui envoie un évènement “.completed” lorsque celle-ci est terminée.

La chose vraiment intéressante avec les Completable, c’est la syntaxe “andThen” qui permet de les chainer.

Voyons cela en action:

C’est excessivement simple à lire, et surtout, cet enchaînement n’a pas besoin d’explications complémentaires, sa lecture se suffit à elle même.

Le séquencement des animations peut même être conditionné par le contexte de l’application:

Mais attendez, nous pouvons aller ENCORE plus loin …

Enchainement magique des animations

Bien que le séquencement Reactif soit une technique très satisfaisante, tout le monde ne veut pas s’appuyer sur de tels paradigmes.

Swift propose une fonctionnalité vraiment très efficace pour racourcir des expressions complexes tout en augmentant leur expressivité : les opérateurs personnalisés.

Cela prend un peu de créativité pour déterminer la bonne façon de procéder. Je me suis donc demandé quelle serait la syntaxe la plus appropriée pour que d’autres développeurs puissent comprendre mon code juste on le regardant rapidement. J’ai aboutit à cette syntaxe:

animation1 ~> animation2 ~> animation3 ~> animation4

Difficile de faire plus simple, non ?

Implémentons cela:

Qu’avons nous déclaré ici ?

  • nous avons défini un opérateur binaire: ~>
  • nous avons défini le comportement de cet opérateur quand il est appliqué à deux UIViewPropertyAnimators

Il s’agit simplement d’une façon de connecter deux UIViewPropertyAnimators ensemble et fixer le démarrage de l’un sur la fin de l’autre.

Utiliser cette syntaxe ne pourrait pas être plus simple 👍

Je suis toujours stupéfié par l’efficacité que Swift amène dans notre flux de développement. J’ai été développeur Java pendant de nombreuses années, et disposer d’une syntaxe si concise est un total bonheur !

J’espère que cela vous donnera de nouvelles idées pour poursuivre encore plus loin dans le séquencement d’animations en Swift.

Vous pouvez d’ores et déjà profiter de l’extension développée ici, puisqu’elle a été intégrée au projet de la RxSwiftCommunity: RxSwiftExt.

A bientôt.

Thibault Wittemberg.

--

--

Thibault Wittemberg
Nuglif
Writer for

My name is Thibault Wittemberg, I am a mobile architect in Montreal and I’m always looking for tips ‘n tricks to improve code awesomeness (twittemb.github.io).