Tag Archives: Xcode

Editer un XIB avec Xcode 8 peut avoir des conséquences inattendues sur le rendu de votre application Xamarin

Apple a rendu disponible la version finale de Xcode 8 courant Septembre et comme beaucoup de développeurs, vous avez probablement effectué la mise à jour si vous voulez pouvoir tirer parti des nouveautés d’iOS 10.

Ayant déjà essuyer les plâtres de ce type de mise à jour par le passé, notamment lors des différentes montées de version de Swift (de 1.0 à 3.0), je laisse désormais passer un peu de temps pour voir si d’autres personnes rencontrent des soucis ou non, même si j’avais déjà testé de manière superficielle cette version sur une 2ème machine de développement à sa sortie.

J’ai donc appliqué le même principe avec cette dernière mise à jour, et j’ai effectué des tests un plus poussés cette semaine pour voir si une application Xamarin.iOS sur laquelle travaille mon équipe, fonctionnait toujours correctement une fois la mise à jour effectuée.

Une interface graphique relativement simple basée sur des XIB

Quand on réalise une application iOS, on peut réaliser l’interface graphique sous 3 formes, en pouvant combiner celles-ci si nécessaire :

  • Par code => Il faut être motivé si les écrans sont complexes
  • A base de XIB => La solution historique utilisée sur iOS depuis ses débuts
  • A base de storyboards => La solution désormais préconisée par Apple

Nous ne débattrons pas ici des avantages et inconvénients de chaque solution et pour l’application sur laquelle nous travaillons, nous avons préféré retenir les XIB avec également la solution par code sur certains composants.

Ci-dessous, vous trouverez l’exemple d’un écran de l’application réalisé par XIB sous Xcode 7, avec l’utilisation de contraintes pour gérer facilement les différentes tailles/résolutions de périphériques. Cet écran contient également un composant entièrement réalisé par code (le calendrier sur l’onglet “Mois”).

Xcode8 - Application avant édition #1  Xcode8 - Application avant édition #2

Ouverture du XIB sous Xcode 8 sans apporter aucune modification…

Après avoir mis à jour les différents outils (Xamarin Studio, Xcode, etc…) sur une machine de développement, j’ai tout simplement ouvert le XIB de cet écran depuis Xcode 8 afin de pouvoir tirer parti des nouveautés de l’environnement Apple (ex: la nouvelle prévisualisation sur différents périphériques).

A l’ouverture du XIB, Xcode 8 fait un certain nombre de modifications dont vous n’avez pas nécessairement conscience, et enregistre automatiquement celles-ci. Le mécanisme de synchronisation entre Xamarin Studio et Xcode fait en sorte que ces modifications soient immédiatement répercutées côté Xamarin à la prochaine compilation de l’application.

… et là c’est le drame une fois l’application compilée et exécutée

Après être retourné sous Xamarin Studio et avoir compiler et exécuter l’application, je retourne sur l’écran en question et là surprise, celui-ci ne fonctionne plus comme auparavant.

Les différences sont subtiles (disparition du liseré rouge sous l’onglet courant, disparition de certaines lignes dans le contrôle calendrier…). Vous pouvez voir ci-dessous le résultat :

Xcode8 - Application après édition #1 Xcode8 - Application après édition #2

Mais que se passe-t-il vu que nous avons juste ouvert le XIB ?

Comme je le disais précédemment, lors de l’ouverture du XIB, Xcode 8 fait quelques modifications sans vous prévenir dans la structure du fichier, dont vous pouvez voir un extrait ci-dessous grâce au client Git que nous utilisons pour nos projets (Atlasssian SourceTree).

Xcode8 - Différences dans le fichier XIB

Comme on peut le voir, certaines lignes ont été ajoutées et/ou modifiées (en vert) et d’autres lignes ont purement et simplement été supprimées (en rouge).

Parmi les lignes supprimées, on retrouve tout ce qui concerne la définition du positionnement par défaut des éléments graphiques dans la vue (les lignes commençant par <rect key=”frame” x=”…” y=”…” width=”…” height=”…” />.

Vous vous demandez peut être pourquoi un tel impact sur le rendu de l’écran ?

Et bien parce que dans celui-ci, certains éléments sont construits par code (le liseré rouge ou le contrôle calendrier) avec du code ressemblant à quelque chose comme ceci :

public override void ViewDidLoad()
{
   base.ViewDidLoad();
   ...

   _selectedBottomLayer = new CALayer();
   _selectedBottomLayer.Frame = new CGRect(0, DayButton.Bounds.Size.Height - SELECTED_LAYER_THICKNESS, DayButton.Bounds.Width, SELECTED_LAYER_THICKNESS);
   View.Layer.AddSublayer(_selectedBottomLayer);

   ...
}

Rien que des choses assez classiques pour les personnes qui font de l’iOS, mais qui ne fonctionnent plus comme auparavant à cause de la modification opérée par Xcode 8.

La suppression de la position par défaut a un impact dans le cycle de vie de construction d’une vue

Le fait que Xcode 8 aie supprimé le positionnement par défaut des éléments n’a pas d’impact sur les éléments positionnés par contraintes, mais pour les éléments positionnés par code et sans contraintes (les contraintes ne peuvent pas toujours être utilisées), cette modification a un impact très important.

Dans l’exemple précédent, le positionnement du liseré rouge est calculé dans la méthode ViewDidLoad. Le problème étant qu’au moment où cette méthode est exécutée dans le cycle de vie iOS, les contraintes appliquées sur les composants graphiques n’ont pas encore été calculées.

Là où au préalable la bouton sur lequel étaient basés les calculs avait un cadre défini comme ceci (x=0, y=0, width=106.5, height=45), il n’a désormais plus de positionnement par défaut ce qui fait qu’au moment ou la méthode ViewDidLoad s’exécute, le bouton a les caractéristiques suivantes (x=0, y=0, width=1000, height=1000).

Donc le liseré rouge est bien construit mais avec des coordonnées totalement farfelues qui le positionnent en dehors de la zone visible de l’écran. Le problème est le même pour les lignes qui ont disparues du contrôle calendrier.

Quelle est la solution pour résoudre les soucis dans ce cas ?

La solution est relativement simplement à mettre en oeuvre mais peut nécessiter de revoir complètement le cycle de vie qui a été utilisé dans les différents écrans.

Puisque le calcul de positionnement ne peut être effectué correctement au moment du ViewDidLoad, il suffit de le faire à un moment plus opportun.

Il n’y a pas une réponse qui pourra répondre à tous les cas possibles que vous pourriez rencontrer mais iOS propose différentes méthodes à surcharger dans lesquelles vous pouvez effectuer les traitements nécessaires à vos besoins.

Parmi les méthodes classiques que l’on a l’habitude d’utiliser pour effectuer les traitements nécessaires à des calculs de positionnement, on retrouve celles-ci (liste non exhaustive) :

public override void UpdateViewConstraints()
{
}

public override void ViewDidAppear(bool animated)
{
}

public override void ViewDidLayoutSubviews()
{
}

public override void ViewWillAppear(bool animated)
{
}

public override void ViewWillLayoutSubviews()
{
}

En conclusion, même si dans la plupart des cas la montée de version de Xcode n’aura pas d’impact sur vos applications, il peut arriver que dans certains cas cela ne soit pas si transparent que cela.

Que Xcode 8 supprime des données du XIB est une chose qui parait assez logique, certainement pour privilégier le système de contraintes créé par Apple, mais le fait que cela change le comportement et l’affichage d’une application qui fonctionnait sans soucis jusque là, est plus dérangeant sur le fond.

On ne répètera jamais assez qu’avant de faire une montée de version quelle qu’elle soit (outils de développement, packages NuGet…), la mise en place de tests approfondis (unitaires, graphiques, etc…) pour vérifier tous les impacts est un élément crucial pour la qualité de vos applications.

Advertisements