Previously, sur PurpleGirafe

Lors de la création de notre première app, nous nous étions laissé sur une problématique : le positionnement des éléments sur l'écran sur différents modèle d'IPhone.

Et oui, nous avions positionné nos différents éléments en prenant comme modèle un IPhone 8 et en donnant des valeurs absolues à tout ce petit monde.
Mais chaque téléphone n'ayant pas la même résolution d'écran, va forcement afficher ce qu'on lui demande sans se préoccuper de savoir si c'est correctement positionné pour l'utilisateur.

C'est là qu'intervient votre meilleur ami, l'Autolayout.

Notre application sans Autolayout

Dans notre exemple ci-dessous, nous identifions parfaitement le problème.
Il suffit de passer le téléphone du mode portrait au mode paysage et notre TextView n'est pas correctement positionné.

Le soucis est exactement le même sur un iphone X ou sur un iPad.

Dans le Size Inspector, dans la section View, les parametre de position (x, y, width et height) sont écrit en dur : Width: 343

Or l'iphone 8 a un écran de 375 x 667

Le SDK fait donc simplement ce qu'on lui demande. Afficher un TextView sur une largeur de 343 points. Et ce quelque soit la largeur disponible sur l'écran.

Wait, c'est quoi ces chiffres ?

375 x 667, Je croyais que mon IPhone 8 était rétina et avait une résolution plus grande que ça ! - Lecteur attentif

L'unité utilisé dans XCode est le point.
Selon le téléphone, un point n'a pas la même valeur. Un point, pour faire simple, c'est une unité dessinée à l'écran en utilisant entre 1 et x points.

Si on regarde les caractéristiques techniques de notre IPhone 8, sa résolution en pixel est de 1 334 x 750 pixels.

Cela fait donc 1 point = 2 pixels de coté.

Sur un IPhone XS Max, 2 436 x 1 125, donc 1 point = 4 pixels de côté (soit 16 pixels utilisés pour le rendu d'un point).

Plus l'écran à une haute résolution, plus on va utiliser de pixels pour afficher un point.

Principe de fonctionnement

Le principe de base de l'Autolayout, est d'appliquer des contraintes aux différents éléments qui composent notre application.
Les écrans des différents appareils étant différents, il est compliqué de pouvoir positionner au pixel chaque éléments en leur donnant de façon classique une position fixe et une taille fixe.

L'idée derrière l'Autolayout est donc d'appliquer des règles qui ne vont pas se soucier de la taille de l'écran lors de la conception, et qui pourront s'adapter a tous les appareils, même ceux qui ne sont pas encore sur le marché.

Par exemple, si nous souhaitons positionner un élément qui prend toute la largeur de l'écran, en conservant un léger espacement à gauche et à droite, nous allons appliquer 3 contraintes : * Position a gauche : 16 points par rapport a l'écran * Position a droite : 16 points par rapport a l'écran * Position en haut : 16 points par rapport a l'écran * Hauteur : 32 points

Grâce à ces 4 règles, notre élément sera toujours a la même position sur tous les écran et prendra toujours la largeur maximale de l'écran.

Les outils

Size Inspector

Le Size Inspector est l'onglet dans la colonne de droite qui va contenir toutes les informations sur la taille et la position de votre élément actuellement sélectionné dans la colonne de gauche "Document Outline"

Il est accessible en ouvrant la colonne de droite des "Inspectors" et en cliquant sur la petite icône en forme de règle graduée.

Safe Area

La Safe Area (Zone de sureté) est une autre notion de la programmation sur IPhone (IPad).

La Safe Area est la zone de l'écran dans laquelle votre application peut se déployer sans impacter l'expérience utilisateur car cette zone est garantie libre de tout autre composant natif du téléphone.

Si on sélectionne la Safe Area sur un IPhone avant les modèles X, il s'agit en réalité de tout l'écran à l'exception de la barre de statut en haut.

A contrario, sur un Iphone X, la Safe Area représente une zone réduite également vers le bas pour que l'utilisateur puisse utiliser le bouton home tactile.

Les contraintes

Pour afficher de façon précise nos éléments, nous allons donc utiliser un système de contraintes.

Une contrainte c'est une règle qui va dire à un composant :

Je souhaite que ta position en X soit toujours à un certain nombre de points de tel élément.

Pour créer une contrainte, XCode fonctionne comme les IBOutlets et les IBActions, sur le clavier, appuyez sur la touche Ctrl et glisser de l'élément que vous souhaitez positionner, vers l'élément que vous souhaitez utiliser comme cible.
Cela va afficher un petit trait bleu et au moment de lâcher le clic, une pop-up va s'afficher avec différentes options.

Il suffit ensuite de cliquer sur la contrainte que l'on souhaite appliquer et c'est en place.

Il est également possible de créer des contraintes depuis la colonne de gauche.

Astuce
On peut définir plusieurs contraintes en maintenant la touche Shift (Maj) pendant que l'on clique.
Par contre on ne choisi pas "Safe Area", mais "View" pour positionner par rapport à l'écran.

Les contraintes disponibles

  • Leading Space : Espacement entre la gauche de notre composant et de la contrainte
  • Trailing Space : Espacement entre la droite de notre composant et de la contrainte
  • Top Space : Espacement entre le haut de notre composant et de la contrainte
  • Bottom Space : Espacement entre le bas de notre composant et de la contrainte
  • Center Horizontally : Centre notre composant dans son conteneur en donnant le même espace a droite que a gauche.
  • Center Vertically : Centre notre composant dans son conteneur en donnant le même espace en haut et en bas.
  • Equal Widths : Fixe la largeur
  • Equal Height : Fixe la hauteur
  • Aspect ratio : Conserve un ratio largeur/hauteur défini.

Modification d'une contraintes

Je vais donc dire à mon TextView de se coller a gauche et a droite de l'écran grâce à un Trailing Space et Leading Space.

Une fois mes contraintes ajoutées, elle sont visibles dans le Size Inspector, ainsi que dans un nouvel element de notre Outline a gauche.

Pour les modifier, 2 possibilités, soit on clic sur edit soit on double clique pour avoir une fenêtre avec plus d'option.

Si je désire que mon élément de texte soit à 40 points de chaque coté, c'est donc rapide à faire.

Edit > 40 > Entrer

Et c'est visible instantanément.

Si on repasse notre aperçu en paysage, notre TextView respecte toujours ses contraintes, a savoir, 40 point a gauche et a droite.

Trop peu de contraintes

À partir du moment où vous utilisez les contraintes, il faut être méthodique, car XCode aura besoin d'avoir assez de contrainte pour afficher l'élément de façon certaine, mais il ne faut pas non plus que des contraintes se contredisent.

Dans mes captures précédents, vous aurez remarqué 2 choses * Les contraintes sont visibles * Mon TextView a des contours rouges en haut et en bas.

Ces contours rouges signifient qu'il n'est pas en mesure de calculer de contrainte pour de coté.
Il ne sait pas comment positionner le "Top" de notre TextView car aucune contrainte ne lui a été définie.

Il ne va pas planter au lancement de l'application, cependant l'affichage n'est pas optimisé et selon les cas peut conduire à des soucis d'affichage.

Pour régler ce soucis, je vais ajouter toute mes contraintes sur tout mes composants.

Une bonne pratique est de partir du haut vers le bas, et de la gauche vers la droite pour ne rien oublier.

Résultat final

J'ai volontairement ajouté une couleur de fond a tous les elements pour que vous puissiez bien distinguer les différents elements.

On a une application qui fonctionne parfaitement sur tous les écrans.

Pas mal non ?

Trop de contraintes

A contrario, si des contraintes se contredisent, le SDK va être confus et ne va pas savoir comment afficher.

J'ai par exemple ajouté une contrainte de largeur à 120 point.

Mon écran fait donc 667 points, le TextView doit être placé à 40 du bord gauche, faire 120 et être à 40 du bord droit.
Pour XCode ce n'est pas possible, je vais donc retirer ma contrainte de largeur, car je souhaite que ma largeur soit dynamique en fonction de l'espace disponible sur l'ecran.

Hugging, Compression...

Durant la mise en place de mes contraintes, je suis tombé sur un soucis que vous rencontrerez souvent. Mes contraintes ne se contredisent pas, je n'en ai ni trop, ni trop peu, mais XCode me signale une erreur.

Les erreurs sont signalées par une petite icône rouge dans l'Outline.

XCode nous explique qu'il ne sait pas comment il doit traiter la largeur des elements TextInput et Button.

Imaginez les contraintes comme des élastiques.
Les composants sont accroché a gauche et a droite de l'écran, et entre eux par des contraintes.
Certaines contraintes ayant une taille fixe (leading, trailing, ll faut que les composants (TextInput et Button) s'étirent pour occuper tout l'espace.

Pour cela il y a 2 notions à comprendre.

Hugging

A ce stade, les 2 élastiques ont les même caractéristiques.
Nous allons faire en sorte que le bouton "+" ne change pas de taille et que ce soit le TextInput.

Pour cela nous allons dire au TextInput que sa capacité à résister est plus faible et qu'il va céder en premier. On va réduire son Hugging horizontal a 249.

Que l'on mette 1 ou 249, cela ne change pas. Il faut juste qu'il soit moins résistant que le bouton qui est a 250.

On aurait très bien pu mettre 251 au Button à la place.

Compression

A l'inverse, la compression est la force d'un élément a résister à la nécessité d'être réduit.

Nous n'avons pas eu à l'utiliser dans notre exemple mais son application est identique au hugging.

Conclusion.

Ainsi s'achève notre second opus.

J'espère que vous aurez appris des choses et que vous les mettrez en pratique.

Si vous avez des question, n'hésitez pas a rejoindre notre communauté sur le forum.