Exécution d'animations iOS sur des vues avec UIKit et UIView
Publié: 2022-03-10Je suis développeur iOS depuis plus d'une décennie maintenant et j'ai rarement vu des articles qui regroupent toutes les façons possibles d'effectuer des animations dans iOS. Cet article vise à être une introduction aux animations iOS dans le but de couvrir de manière exhaustive les différentes façons de faire de même.
Compte tenu de l'étendue du sujet, nous couvririons succinctement chaque partie à un niveau assez élevé. L'objectif est d'éduquer le lecteur avec un ensemble de choix pour ajouter des animations à son application iOS.
Avant de commencer avec des sujets liés à iOS, examinons brièvement la vitesse d'animation.
Animation à 60FPS
Généralement dans les vidéos, chaque image est représentée par une image et la fréquence d'images détermine le nombre d'images inversées dans la séquence. C'est ce qu'on appelle « images par seconde » ou FPS.
FPS détermine le nombre d'images fixes retournées en une seconde, ce qui signifie littéralement que plus le nombre d'images/cadres est élevé, plus de détails/informations sont affichés dans la vidéo. Cela vaut également pour les animations.
FPS est généralement utilisé pour déterminer la qualité des animations. Il y a une opinion populaire selon laquelle toute bonne animation devrait fonctionner à 60 ips ou plus – tout ce qui est inférieur à 60 ips semblerait un peu décalé.
Vous voulez voir la différence entre 30FPS et 60FPS ? Vérifie ça!
Avez-vous remarqué la différence ? Les yeux humains peuvent certainement ressentir la gigue à des fps inférieurs. Par conséquent, il est toujours recommandé de s'assurer que toute animation que vous créez respecte la règle de base consistant à fonctionner à 60 images par seconde ou plus. Cela le rend plus réaliste et vivant.
Après avoir examiné FPS, examinons maintenant les différents frameworks iOS de base qui nous permettent de réaliser des animations.
Cadres de base
Dans cette section, nous aborderons les frameworks du SDK iOS qui peuvent être utilisés pour créer des animations de vue. Nous allons parcourir rapidement chacun d'entre eux, en expliquant leur ensemble de fonctionnalités avec un exemple pertinent.
Animations UIKit/UIView
UIView est la classe de base pour toute vue qui affiche du contenu dans les applications iOS.
UIKit, le framework qui nous donne UIView, nous fournit déjà quelques fonctions d'animation de base qui permettent aux développeurs d'en faire plus en faisant moins.
L'API, UIView.animate
, est le moyen le plus simple d'animer des vues puisque les propriétés de n'importe quelle vue peuvent être facilement animées en fournissant les valeurs de propriété dans la syntaxe basée sur les blocs.
Dans les animations UIKit, il est recommandé de modifier uniquement les propriétés animables de UIVIew, sinon il y aura des répercussions là où les animations pourraient amener la vue à se retrouver dans un état inattendu.
animation(avecDurée : animations : achèvement)
Cette méthode prend en compte la durée de l'animation, un ensemble de modifications de propriétés animables de la vue qui doivent être animées. Le bloc d'achèvement donne un rappel lorsque la vue est terminée avec l'exécution de l'animation.
Presque n'importe quel type d'animation comme le déplacement, la mise à l'échelle, la rotation, le fondu, etc. sur une vue peut être réalisé avec cette API unique.
Maintenant, considérez que vous voulez animer un changement de taille de bouton ou que vous voulez qu'une vue particulière zoome sur l'écran. Voici comment nous pouvons le faire en utilisant l'API UIView.animate
:
let newButtonWidth: CGFloat = 60 UIView.animate(withDuration: 2.0) { //1 self.button.frame = CGRect(x: 0, y: 0, width: newButtonWidth, height: newButtonWidth) //2 self.button.center = self.view.center //3 }
Voici ce que nous faisons ici :
- Nous appelons la méthode
UIView.animate
avec une valeur de durée qui lui est transmise et qui représente la durée pendant laquelle l'animation, décrite dans le bloc, doit s'exécuter. - Nous définissons la nouvelle image du bouton qui doit représenter l'état final de l'animation.
- Nous définissons le centre du bouton avec le
center
de sa vue d'ensemble afin qu'il reste au centre de l'écran.
Le bloc de code d'animation ci-dessus doit déclencher l'animation de l'image du bouton passant de l'image actuelle :
Width = 0, Height = 0
Au cadre final :
Width = Height = newButtonWidth
Et voici à quoi ressemblerait l'animation :
animerAvecDurée
Cette méthode est comme une extension de la méthode d'animation où vous pouvez faire tout ce que vous pouvez effectuer dans l'API précédente avec certains comportements physiques ajoutés aux animations de vue.
Par exemple, si vous souhaitez obtenir des effets d'amortissement de ressort dans l'animation que nous avons réalisée ci-dessus, voici à quoi ressemblerait le code :
let newButtonWidth: CGFloat = 60 UIView.animate(withDuration: 1.0, //1 delay: 0.0, //2 usingSpringWithDamping: 0.3, //3 initialSpringVelocity: 1, //4 options: UIView.AnimationOptions.curveEaseInOut, //5 animations: ({ //6 self.button.frame = CGRect(x: 0, y: 0, width: newButtonWidth, height: newButtonWidth) self.button.center = self.view.center }), completion: nil)
Voici l'ensemble de paramètres que nous utilisons :
-
duration
Représente la durée de l'animation déterminant la durée d'exécution du bloc de code. -
delay
Représente le délai initial que nous voulons avoir avant le début de l'animation. -
SpringWithDamping
Représente la valeur de l'effet élastique que nous voulons que la vue se comporte. La valeur doit être comprise entre 0 et 1. Plus la valeur est faible, plus l'oscillation du ressort est élevée. -
velocity
Représente la vitesse à laquelle l'animation doit démarrer. -
options
Type de courbe d'animation que vous souhaitez appliquer à l'animation de votre vue. - Enfin, le bloc de code où nous définissons le cadre du bouton qui doit être animé. C'est la même chose que l'animation précédente.
Et voici à quoi ressemblerait l'animation avec la configuration d'animation ci-dessus :
UIViewPropertyAnimatorUIViewPropertyAnimator
Pour un peu plus de contrôle sur les animations, UIViewPropertyAnimator
est pratique car il nous permet de mettre en pause et de reprendre les animations. Vous pouvez avoir un timing personnalisé et faire en sorte que votre animation soit interactive et interruptible. Ceci est très utile lors de l'exécution d'animations qui peuvent également interagir avec les actions de l'utilisateur.
Le geste classique "Glisser pour déverrouiller" et l'animation de fermeture/développement de la vue du lecteur (dans l'application Musique) sont des exemples d'animations interactives et interruptibles. Vous pouvez commencer à déplacer une vue avec votre doigt, puis le relâcher et la vue reviendra à sa position d'origine. Vous pouvez également capturer la vue pendant l'animation et continuer à la faire glisser avec votre doigt.
Voici un exemple simple de la façon dont nous pourrions réaliser l'animation en utilisant UIViewPropertyAnimator
:
let newButtonWidth: CGFloat = 60 let animator = UIViewPropertyAnimator(duration:0.3, curve: .linear) { //1 self.button.frame = CGRect(x: 0, y: 0, width: newButtonWidth, height: newButtonWidth) self.button.center = self.view.center } animator.startAnimation() //2
Voici ce que nous faisons :
- Nous appelons l'API
UIViewProperty
en passant la durée et la courbe d'animation. - Contrairement aux deux API UIView.animate ci-dessus, l'animation ne démarrera que si vous la spécifiez vous-même, c'est-à-dire que vous contrôlez totalement le processus/flux d'animation complet.
Maintenant, disons que vous voulez encore plus de contrôle sur les animations. Par exemple, vous souhaitez concevoir et contrôler chaque image de l'animation. Il existe une autre API pour cela, animateKeyframes
. Mais avant de nous y plonger, regardons rapidement ce qu'est une image, dans une animation.
Qu'est-ce qu'un frame
?
Une collection de changements/transitions de cadre de la vue, de l'état de départ à l'état final, est définie comme une animation
et chaque position de la vue pendant l'animation est appelée frame
.
animer les images clés
Cette API fournit un moyen de concevoir l'animation de telle sorte que vous puissiez définir plusieurs animations avec des synchronisations et des transitions différentes. Après cela, l'API intègre simplement toutes les animations dans une expérience transparente.
Disons que nous voulons déplacer notre bouton sur l'écran de manière aléatoire. Voyons comment nous pouvons utiliser l'API d'animation d'images clés pour le faire.
UIView.animateKeyframes(withDuration: 5, //1 delay: 0, //2 options: .calculationModeLinear, //3 animations: { //4 UIView.addKeyframe( //5 withRelativeStartTime: 0.25, //6 relativeDuration: 0.25) { //7 self.button.center = CGPoint(x: self.view.bounds.midX, y: self.view.bounds.maxY) //8 } UIView.addKeyframe(withRelativeStartTime: 0.5, relativeDuration: 0.25) { self.button.center = CGPoint(x: self.view.bounds.width, y: start.y) } UIView.addKeyframe(withRelativeStartTime: 0.75, relativeDuration: 0.25) { self.button.center = start } })
Voici la répartition :
-
duration
Appelez l'API en passant la durée de l'animation. -
delay
Durée du retard initial de l'animation. -
options
Le type de courbe d'animation que vous souhaitez appliquer à l'animation de votre vue. -
animations
Bloc qui prend toutes les animations d'images clés conçues par le développeur/utilisateur. -
addKeyFrame
Appelez l'API pour concevoir chaque animation. Dans notre cas, nous avons défini chaque déplacement du bouton. Nous pouvons avoir autant d'animations de ce type que nous en avons besoin, ajoutées au bloc. -
relativeStartTime
Définit l'heure de début de l'animation dans la collection du bloc d'animation. -
relativeDuration
Définit la durée globale de cette animation spécifique. -
center
Dans notre cas, nous modifions simplement la propriété center du bouton pour déplacer le bouton sur l'écran.
Et voici à quoi ressemblent les animations finales :
CoreAnimation
Toute animation basée sur UIKit est traduite en interne en animations de base. Ainsi, le framework Core Animation agit comme une couche de support ou une colonne vertébrale pour toute animation UIKit. Par conséquent, toutes les API d'animation UIKit ne sont rien d'autre que des couches encapsulées des principales API d'animation d'une manière facilement consommable ou pratique.
Les API d'animation UIKit ne fournissent pas beaucoup de contrôle sur les animations qui ont été exécutées sur une vue car elles sont principalement utilisées pour les propriétés animables de la vue. Par conséquent, dans de tels cas, où vous avez l'intention de contrôler chaque image de l'animation, il est préférable d'utiliser directement les API d'animation principales sous-jacentes. Alternativement, les animations UIView et les animations principales peuvent également être utilisées conjointement.
UIView + animation de base
Voyons comment nous pouvons recréer la même animation de changement de bouton tout en spécifiant la courbe de synchronisation à l'aide des API UIView et Core Animation.
Nous pouvons utiliser les fonctions de synchronisation de CATransaction
, qui vous permettent de spécifier et de contrôler la courbe d'animation.
Regardons un exemple d'animation de changement de taille de bouton avec son rayon d'angle utilisant la fonction de synchronisation de CATransaction
et une combinaison d'animations UIView :
let oldValue = button.frame.width/2 let newButtonWidth: CGFloat = 60 /* Do Animations */ CATransaction.begin() //1 CATransaction.setAnimationDuration(2.0) //2 CATransaction.setAnimationTimingFunction(CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)) //3 // View animations //4 UIView.animate(withDuration: 1.0) { self.button.frame = CGRect(x: 0, y: 0, width: newButtonWidth, height: newButtonWidth) self.button.center = self.view.center } // Layer animations let cornerAnimation = CABasicAnimation(keyPath: #keyPath(CALayer.cornerRadius)) //5 cornerAnimation.fromValue = oldValue //6 cornerAnimation.toValue = newButtonWidth/2 //7 button.layer.cornerRadius = newButtonWidth/2 //8 button.layer.add(cornerAnimation, forKey: #keyPath(CALayer.cornerRadius)) //9 CATransaction.commit() //10
Voici la répartition :
-
begin
Représente le début du bloc de code d'animation. -
duration
Durée globale de l'animation. -
curve
Représente la courbe de synchronisation qui doit être appliquée à l'animation. -
UIView.animate
Notre première animation pour changer le cadre du bouton. -
CABasicAnimation
Nous créons l'objetCABasicAnimation
en faisant référence aucornerRadius
du bouton comme chemin de clé puisque c'est ce que nous voulons animer. De même, si vous souhaitez avoir un contrôle de niveau granulaire sur les animations d'images clés, vous pouvez utiliser la classeCAKeyframeAnimation
. -
fromValue
Représente la valeur de départ de l'animation, c'est-à-dire la valeur initialecornerRadius
du bouton à partir duquel l'animation doit démarrer. -
toValue
Représente la valeur finale de l'animation, c'est-à-dire la valeur finalecornerRadius
du bouton où l'animation doit se terminer. -
cornerRadius
Nous devons définir la propriétécornerRadius
du bouton avec la valeur finale de l'animation, sinon la valeur cornerRadius du bouton reviendra automatiquement à sa valeur initiale une fois l'animation terminée. -
addAnimation
Nous attachons l'objet d'animation qui contient la configuration de l'ensemble du processus d'animation à la couche en représentant le Keypath pour lequel l'animation doit être effectuée. -
commit
Représente la fin du bloc de code d'animation et démarre l'animation.
Voici à quoi ressemblerait l'animation finale :
Ce blog est une excellente lecture pour vous aider à créer des animations plus avancées car il vous guide parfaitement à travers la plupart des API du framework Core Animation avec des instructions vous guidant à chaque étape du processus.
UIKitDynamics
UIKit Dynamics est le moteur physique d'UIKit qui vous permet d'ajouter n'importe quel comportement physique comme la collision, la gravité, la poussée, l'accrochage, etc., aux commandes UIKit.
UIKitDynamicAnimator
Il s'agit de la classe d'administration du framework UIKit Dynamics qui régule toutes les animations déclenchées par un contrôle d'interface utilisateur donné.
UIKitDynamicBehaviorUIKitDynamicBehavior
Il vous permet d'ajouter n'importe quel comportement physique à un animateur, ce qui lui permet ensuite d'agir sur la vue qui lui est attachée.
Les différents types de comportements pour UIKitDynamics incluent :
-
UIAttachmentBehavior
-
UICollisionBehavior
-
UIFieldBehavior
-
UIGravityBehavior
-
UIPushBehavior
-
UISnapBehavior
L'architecture de UIKitDynamics ressemble à ceci. Notez que les éléments 1 à 5 peuvent être remplacés par une seule vue.
Appliquons un comportement physique à notre bouton. Nous verrons comment appliquer la gravité au bouton pour qu'il nous donne l'impression d'avoir affaire à un objet réel.
var dynamicAnimator : UIDynamicAnimator! var gravityBehavior : UIGravityBehavior! dynamicAnimator = UIDynamicAnimator(referenceView: self.view) //1 gravityBehavior = UIGravityBehavior(items: [button]) //2 dynamicAnimator.addBehavior(gravityBehavior) //3
Voici la répartition :
-
UIKitDynamicAnimator
Nous avons créé un objetUIKitDynamicAnimator
qui agit comme un orchestrateur pour effectuer des animations. Nous avons également adopté la superview de notre bouton comme vue de référence. -
UIGravityBehavior
Nous avons créé un objetUIGravityBehavior
et passé notre bouton dans les éléments du tableau sur lesquels ce comportement est injecté. -
addBehavior
Nous avons ajouté l'objet gravité à l'animateur.
Cela devrait créer une animation comme indiqué ci-dessous :
Il faut dire à l'animateur de considérer le bas de l'écran comme le sol. C'est là queUICollisionBehavior
entre en scène.var dynamicAnimator : UIDynamicAnimator! var gravityBehavior : UIGravityBehavior! var collisionBehavior : UICollisionBehavior! dynamicAnimator = UIDynamicAnimator(referenceView: self.view) //1 gravityBehavior = UIGravityBehavior(items: [button]) //2 dynamicAnimator.addBehavior(gravityBehavior) //3 collisionBehavior = UICollisionBehavior(items: [button]) //4 collisionBehavior.translatesReferenceBoundsIntoBoundary = true //5 dynamicAnimator.addBehavior(collisionBehavior) //6
-
UICollisionBehavior
Nous avons créé un objetUICollisionBehavior
et transmis le bouton afin que le comportement soit ajouté à l'élément. -
translatesReferenceBoundsIntoBoundary
L'activation de cette propriété indique à l'animateur de prendre la limite des vues de référence comme fin, qui est le bas de l'écran dans notre cas. -
addBehavior
Nous avons ajouté un comportement de collision à l'animateur ici.
Maintenant, notre bouton devrait toucher le sol et rester immobile comme indiqué ci-dessous :
C'est plutôt chouette, n'est-ce pas ?
Maintenant, essayons d'ajouter un effet de rebond pour que notre objet semble plus réel. Pour ce faire, nous allons utiliser la classeUIDynamicItemBehavior
.var dynamicAnimator : UIDynamicAnimator! var gravityBehavior : UIGravityBehavior! var collisionBehavior : UICollisionBehavior! var bouncingBehavior : UIDynamicItemBehavior! dynamicAnimator = UIDynamicAnimator(referenceView: self.view) //1 gravityBehavior = UIGravityBehavior(items: [button]) //2 dynamicAnimator.addBehavior(gravityBehavior) //3 collisionBehavior = UICollisionBehavior(items: [button]) //4 collisionBehavior.translatesReferenceBoundsIntoBoundary = true //5 dynamicAnimator.addBehavior(collisionBehavior) //6 //Adding the bounce effect bouncingBehavior = UIDynamicItemBehavior(items: [button]) //7 bouncingBehavior.elasticity = 0.75 //8 dynamicAnimator.addBehavior(bouncingBehavior) //9
-
UIDynamicItemBehavior
Nous avons créé un objetUIDynamicItemBehavior
et transmis le bouton afin que le comportement soit ajouté à l'élément. -
elasticity
La valeur doit être comprise entre 0 et 1, elle représente l'élasticité, c'est-à-dire le nombre de fois que l'objet doit rebondir sur et hors du sol lorsqu'il est touché. C'est là que la magie opère - en ajustant cette propriété, vous pouvez différencier différents types d'objets comme des balles, des bouteilles, des objets durs, etc. -
addBehavior
Nous avons ajouté un comportement de collision à l'animateur ici.
Maintenant, notre bouton devrait rebondir lorsqu'il touche le sol comme indiqué ci-dessous :
Ce référentiel est très utile et montre tous les comportements UIKitDynamics en action. Il fournit également un code source pour jouer avec chaque comportement. Cela, à mon avis, devrait servir de liste complète de façons d'effectuer des animations iOS sur les vues !
Dans la section suivante, nous examinerons brièvement les outils qui nous aideront à mesurer les performances des animations. Je vous recommanderais également de rechercher des moyens d'optimiser votre build Xcode, car cela vous fera gagner énormément de temps de développement.
L'optimisation des performances
Dans cette section, nous examinerons les moyens de mesurer et d'ajuster les performances des animations iOS. En tant que développeur iOS, vous avez peut-être déjà utilisé des instruments Xcode tels que les fuites de mémoire et les allocations pour mesurer les performances de l'application globale. De même, il existe des instruments qui peuvent être utilisés pour mesurer les performances des animations.
Instrument Core Animation
Essayez l'instrument Core Animation
et vous devriez pouvoir voir le FPS fourni par l'écran de votre application. C'est un excellent moyen de mesurer les performances/la vitesse de toute animation rendue dans votre application iOS.
Dessin
Le FPS est considérablement réduit dans l'application qui affiche un contenu lourd comme des images avec des effets comme des ombres. Dans de tels cas, au lieu d'attribuer l' Image directement à la propriété image de UIImageView
, essayez de dessiner l'image séparément dans un contexte à l'aide des API Core Graphics. Cela réduit excessivement le temps d'affichage de l'image en exécutant la logique de décompression de l'image de manière asynchrone lorsqu'elle est effectuée dans un thread séparé au lieu du thread principal.
Rastérisation
La rastérisation est un processus utilisé pour mettre en cache des informations de couche complexes afin que ces vues ne soient pas redessinées à chaque fois qu'elles sont rendues. Le rafraîchissement des vues est la principale cause de la réduction des FPS et, par conséquent, il est préférable d'appliquer la rastérisation sur les vues qui vont être réutilisées plusieurs fois.
Emballer
Pour conclure, j'ai également résumé une liste de ressources utiles pour les animations iOS. Vous pouvez trouver cela très pratique lorsque vous travaillez sur des animations iOS. De plus, vous pouvez également trouver cet ensemble d'outils de conception utile comme étape (de conception) avant de vous plonger dans les animations.
J'espère avoir pu couvrir autant de sujets que possible autour des animations iOS. S'il y a quelque chose que j'ai pu manquer dans cet article, faites-le moi savoir dans la section des commentaires ci-dessous et je serais heureux de faire l'ajout!