Storyboards unifiés dans Xamarin.iOS

iOS 8 inclut un nouveau mécanisme plus simple à utiliser pour créer l’interface utilisateur : le storyboard unifié. Avec un seul storyboard pour couvrir toutes les différentes tailles d’écran matérielles, des vues rapides et réactives peuvent être créées dans un style « design-once, use-many ».

Comme le développeur n’a plus besoin de créer un storyboard distinct et spécifique pour les appareils iPhone et iPad, il a la possibilité de concevoir des applications avec une interface commune, puis de personnaliser cette interface pour différentes classes de taille. De cette façon, une application peut être adaptée aux forces de chaque facteur de forme et chaque interface utilisateur peut être paramétrée pour offrir la meilleure expérience.

Classes de taille

Avant iOS 8, le développeur utilisait UIInterfaceOrientation et UIInterfaceIdiom pour différencier les modes portrait et paysage, ainsi qu’entre les appareils iPhone et iPad. Dans iOS8, l’orientation et l’appareil sont déterminés à l’aide de classes de taille.

Les appareils sont définis par des classes de taille, dans des axes verticaux et horizontaux, et il existe deux types de classes de taille dans iOS 8 :

  • Normal : il s’agit d’une grande taille d’écran (comme un iPad) ou d’un gadget qui donne l’impression d’une grande taille (comme un UIScrollView
  • Compact : il s’agit d’appareils plus petits (comme un iPhone). Cette taille prend en compte l’orientation de l’appareil.

Si les deux concepts sont utilisés ensemble, le résultat est une grille de 2 x 2 qui définit les différentes tailles possibles qui peuvent être utilisées dans les deux orientations différentes, comme illustré dans le diagramme suivant :

Grille 2 x 2 qui définit les différentes tailles possibles qui peuvent être utilisées dans les orientations Régulière et Compacte

Le développeur peut créer un contrôleur d’affichage qui utilise l’une des quatre possibilités qui entraîneraient des dispositions différentes (comme indiqué dans les graphiques ci-dessus).

Classes de taille iPad

L’iPad, en raison de sa taille, a une taille de classe régulière pour les deux orientations.

Classes de taille iPad

Classes de taille iPhone

L’iPhone a différentes classes de taille en fonction de l’orientation de l’appareil :

Classes de taille iPhone

  • Lorsque l’appareil est en mode portrait, l’écran a une classe compacte horizontalement et verticalement régulière
  • Lorsque l’appareil est en mode paysage, les classes d’écran sont inversées à partir du mode portrait.

Classes de taille iPhone 6 Plus

Les tailles sont identiques à celles des iPhone précédents en orientation portrait, mais différentes dans le paysage :

Classes de taille iPhone 6 Plus

Étant donné que l’iPhone 6 Plus a un écran assez grand, il est en mesure d’avoir une classe de taille de largeur régulière en mode Paysage.

Prise en charge d’une nouvelle échelle d’écran

L’iPhone 6 Plus utilise un nouvel écran Retina HD avec un facteur de mise à l’échelle de l’écran de 3.0 (trois fois la résolution d’écran d’origine de l’iPhone). Pour offrir la meilleure expérience possible sur ces appareils, incluez de nouvelles illustrations conçues pour cette mise à l’échelle de l’écran. Dans Xcode 6 et versions ultérieures, les catalogues de ressources peuvent inclure des images de tailles 1x, 2x et 3x ; Ajoutez simplement les nouvelles ressources d’image et iOS choisira les ressources appropriées lors de l’exécution sur un iPhone 6 Plus.

Le comportement de chargement d’images dans iOS reconnaît également un @3x suffixe sur les fichiers image. Par exemple, si le développeur inclut une ressource d’image (à différentes résolutions) dans le bundle de l’application avec les noms de fichiers suivants : MonkeyIcon.png, MonkeyIcon@2x.pnget MonkeyIcon@3x.png. Sur l’iPhone 6 Plus, l’image MonkeyIcon@3x.png est utilisée automatiquement si le développeur charge une image à l’aide du code suivant :

UIImage icon = UIImage.FromFile("MonkeyImage.png");

Écrans de lancement dynamique

Le fichier d’écran de lancement s’affiche sous la forme d’un écran de démarrage pendant le lancement d’une application iOS pour fournir des commentaires à l’utilisateur indiquant que l’application est en train de démarrer. Avant iOS 8, le développeur devait inclure plusieurs Default.png ressources d’image pour chaque type d’appareil, orientation et résolution d’écran sur lequel l’application s’exécuterait.

Nouveau dans iOS 8, le développeur peut créer un fichier atomique .xib unique dans Xcode qui utilise des classes de disposition et de taille automatiques pour créer un écran de lancement dynamique qui fonctionnera pour chaque appareil, résolution et orientation. Cela réduit non seulement la quantité de travail requise du développeur pour créer et gérer toutes les ressources d’image requises, mais cela réduit également la taille du bundle installé de l’application.

Caractéristiques

Les caractéristiques sont des propriétés qui peuvent être utilisées pour déterminer comment une disposition change à mesure que son environnement change. Ils se composent d’un ensemble de propriétés (le HorizontalSizeClass et basé sur UIUserInterfaceSizeClass), ainsi que de l’idiome de l’interface ( UIUserInterfaceIdiom) et de l’échelle VerticalSizeClass d’affichage.

Tous les états ci-dessus sont encapsulés dans un conteneur qu’Apple appelle une collection trait ( UITraitCollection), qui contient non seulement les propriétés, mais aussi leurs valeurs.

Environnement trait

Les environnements trait sont une nouvelle interface dans iOS 8 et peuvent retourner une collection trait pour les objets suivants :

  • Écrans ( UIScreens ).
  • Windows ( UIWindows ).
  • Afficher les contrôleurs ( UIViewController ).
  • Vues ( UIView ).
  • Contrôleur de présentation ( UIPresentationController ).

Le développeur utilise la collection trait renvoyée par un environnement Trait pour déterminer la façon dont une interface utilisateur doit être disposée.

Tous les environnements trait constituent une hiérarchie, comme indiqué dans le diagramme suivant :

Diagramme de hiérarchie des environnements de trait

La collection traits que chacun des environnements Trait ci-dessus possède transite, par défaut, du parent à l’environnement enfant.

En plus d’obtenir la collection trait actuelle, l’environnement Trait dispose d’une TraitCollectionDidChange méthode, qui peut être remplacée dans les sous-classes View ou View Controller. Le développeur peut utiliser cette méthode pour modifier l’un des éléments d’interface utilisateur qui dépendent de caractéristiques lorsque ces caractéristiques ont changé.

Collections de traits typiques

Cette section couvre les types typiques de collections de caractéristiques que l’utilisateur rencontrera lors de l’utilisation d’iOS 8.

Voici une collection de traits typique que le développeur peut voir sur un iPhone :

Propriété Valeur
HorizontalSizeClass Compact
VerticalSizeClass Normal
UserInterfaceIdom Téléphone
DisplayScale 2.0

L’ensemble ci-dessus représente une collection de traits qualifiés complets, car elle a des valeurs pour toutes ses propriétés de trait.

Il est également possible d’avoir une collection trait qui manque certaines de ses valeurs (ce qu’Apple appelle Unspecified) :

Propriété Valeur
HorizontalSizeClass Compact
VerticalSizeClass Non spécifié
UserInterfaceIdom Non spécifié
DisplayScale Non spécifié

En règle générale, toutefois, lorsque le développeur demande à l’environnement Trait sa collection Trait, il retourne une collection complète, comme indiqué dans l’exemple ci-dessus.

Si un environnement trait (comme une vue ou un contrôleur d’affichage) ne se trouve pas à l’intérieur de la hiérarchie d’affichage actuelle, le développeur peut récupérer des valeurs non spécifiées pour une ou plusieurs propriétés de trait.

Le développeur obtiendra également une collection trait partiellement qualifiée s’il utilise l’une des méthodes de création fournies par Apple, comme UITraitCollection.FromHorizontalSizeClass, pour créer une collection.

Une opération qui peut être effectuée sur plusieurs collections de traits consiste à les comparer les unes aux autres, ce qui implique de demander à une collection Trait si elle en contient une autre. Ce que l’on entend par Containment , c’est que, pour tout caractère spécifié dans la deuxième collection, la valeur doit correspondre exactement à la valeur de la première collection.

Pour tester deux caractéristiques, utilisez la Contains méthode du UITraitCollection passage de la valeur du trait à tester.

Le développeur peut effectuer les comparaisons manuellement dans du code pour déterminer comment mettre en page les vues ou afficher les contrôleurs. Toutefois, UIKit utilise cette méthode en interne pour fournir certaines de ses fonctionnalités, comme dans le proxy d’apparence, par exemple.

Proxy d’apparence

Le proxy d’apparence a été introduit dans les versions antérieures d’iOS pour permettre aux développeurs de personnaliser les propriétés de leurs vues. Il a été étendu dans iOS 8 pour prendre en charge les collections trait.

Les proxys d’apparence incluent désormais une nouvelle méthode, AppearanceForTraitCollection, qui retourne un nouveau proxy d’apparence pour la collection trait donnée qui a été transmise. Toutes les personnalisations effectuées par le développeur sur ce proxy d’apparence ne prendront effet que sur les vues conformes à la collection trait spécifiée.

En règle générale, le développeur transmet une collection trait partiellement spécifiée à la AppearanceForTraitCollection méthode, telle que celle qui vient de spécifier une classe de taille horizontale de Compact, afin qu’il puisse personnaliser n’importe quelle vue de l’application qui est compacte horizontalement.

Uiimage

Une autre classe à laquelle Apple a ajouté Trait Collection est UIImage. Dans le passé, le développeur devait spécifier une @1X et @2x version de toute ressource graphique bitmap qu’il allait inclure dans l’application (par exemple, une icône).

iOS 8 a été développé pour permettre au développeur d’inclure plusieurs versions d’une image dans un catalogue d’images basé sur une collection trait. Par exemple, le développeur peut inclure une image plus petite pour l’utilisation d’une classe Trait compact et d’une image de taille réelle pour toute autre collection.

Quand l’une des images est utilisée à l’intérieur d’une UIImageView classe, la vue Image affiche automatiquement la version correcte de l’image pour sa collection Trait. Si l’environnement trait change (par exemple, si l’utilisateur bascule l’appareil du portrait au paysage), la vue Image sélectionne automatiquement la nouvelle taille d’image pour qu’elle corresponde à la nouvelle collection Trait et modifie sa taille pour qu’elle corresponde à celle de la version actuelle de l’image affichée.

UIImageAsset

Apple a ajouté une nouvelle classe à iOS 8 appelée UIImageAsset pour donner au développeur encore plus de contrôle sur la sélection d’images.

Une ressource d’image termine toutes les différentes versions d’une image et permet au développeur de demander une image spécifique qui correspond à une collection trait qui a été transmise. Les images peuvent être ajoutées ou supprimées d’une ressource d’image, à la volée.

Pour plus d’informations sur les ressources d’image, consultez la documentation UIImageAsset d’Apple.

Combinaison de collections de caractéristiques

Une autre fonction qu’un développeur peut effectuer sur les collections de caractéristiques consiste à en ajouter deux ensemble qui aboutiront à la collection combinée, où les valeurs non spécifiées d’une collection sont remplacées par les valeurs spécifiées dans une seconde. Cette opération est effectuée à l’aide de la FromTraitsFromCollections méthode de la UITraitCollection classe .

Comme indiqué ci-dessus, si l’un des traits n’est pas spécifié dans l’une des collections de caractéristiques et est spécifié dans une autre, la valeur est définie sur la version spécifiée. Toutefois, si plusieurs versions d’une valeur donnée sont spécifiées, la valeur de la dernière collection traits sera la valeur utilisée.

Contrôleurs d’affichage adaptatif

Cette section décrit en détail comment les contrôleurs de vue et de vue iOS ont adopté les concepts de caractéristiques et de classes de taille pour être automatiquement plus adaptatifs dans les applications du développeur.

Contrôleur de vue fractionnée

L’une des classes de contrôleur d’affichage qui a le plus changé dans iOS 8 est la UISplitViewController classe . Dans le passé, les développeurs utilisaient souvent un contrôleur d’affichage partagé sur la version iPad de l’application, puis ils devaient fournir une version complètement différente de leur hiérarchie d’affichage pour la version iPhone de l’application.

Dans iOS 8, la UISplitViewController classe est disponible sur les deux plateformes (iPad et iPhone), ce qui permet au développeur de créer une hiérarchie de contrôleur de vue qui fonctionnera à la fois pour iPhone et iPad.

Lorsqu’un iPhone est en mode Paysage, le contrôleur de vue fractionnée présente ses affichages côte à côte, comme il le ferait lors de l’affichage sur un iPad.

Substitution de caractéristiques

Les environnements de caractéristiques sont en cascade du conteneur parent vers les conteneurs enfants, comme dans le graphique suivant montrant un contrôleur de vue fractionnée sur un iPad dans l’orientation paysage :

Un contrôleur d’affichage partagé sur un iPad dans l’orientation paysage

Étant donné que l’iPad a une classe de taille régulière dans les orientations horizontales et verticales, le mode Fractionné affiche à la fois les vues master et détails.

Sur un iPhone, où la classe de taille est compacte dans les deux orientations, le contrôleur de vue fractionnée affiche uniquement l’affichage des détails, comme indiqué ci-dessous :

Le contrôleur de vue fractionnée affiche uniquement l’affichage détaillé

Dans une application où le développeur souhaite afficher à la fois la vue master et la vue détaillée sur un iPhone dans l’orientation paysage, le développeur doit insérer un conteneur parent pour le contrôleur d’affichage partagé et remplacer la collection traits. Comme indiqué dans le graphique ci-dessous :

Le développeur doit insérer un conteneur parent pour le contrôleur d’affichage partagé et remplacer la collection traits

Un UIView est défini comme parent du contrôleur d’affichage partagé et la SetOverrideTraitCollection méthode est appelée sur la vue en passant dans une nouvelle collection de traits et ciblant le contrôleur de vue fractionnée. La nouvelle collection traits remplace le , en lui Regularaffectant la HorizontalSizeClassvaleur , de sorte que le contrôleur de vue fractionnée affiche à la fois les vues master et détaillées sur un iPhone dans l’orientation paysage.

Notez que la VerticalSizeClass valeur a été définie unspecifiedsur , ce qui permet d’ajouter la nouvelle collection Trait à la collection traits sur le parent, ce qui donne un Compact VerticalSizeClass pour le contrôleur d’affichage partagé enfant.

Changements de caractéristiques

Cette section examine en détail la façon dont les collections de caractéristiques sont transférées lorsque l’environnement de caractéristiques change. Par exemple, lorsque l’appareil passe du portrait au paysage.

Vue d’ensemble des changements de traits de portrait en paysage

Tout d’abord, iOS 8 effectue une configuration pour préparer la transition. Ensuite, le système anime l’état de transition. Enfin, iOS 8 nettoie tous les états temporaires requis pendant la transition.

iOS 8 fournit plusieurs rappels que le développeur peut utiliser pour participer au changement de trait, comme indiqué dans le tableau suivant :

Phase Rappel Description
Programme d’installation
  • WillTransitionToTraitCollection
  • TraitCollectionDidChange
  • Cette méthode est appelée au début d’une modification de trait avant qu’une collection traits soit définie sur sa nouvelle valeur.
  • La méthode est appelée lorsque la valeur de la collection traits a changé, mais avant qu’une animation n’ait lieu.
Animation WillTransitionToTraitCollection Le coordinateur de transition qui est passé à cette méthode a une AnimateAlongside propriété qui permet au développeur d’ajouter des animations qui seront exécutées avec les animations par défaut.
Nettoyage WillTransitionToTraitCollection Fournit une méthode permettant aux développeurs d’inclure leur propre code de nettoyage après la transition.

La WillTransitionToTraitCollection méthode est idéale pour animer des contrôleurs d’affichage avec les modifications de la collection traits. La WillTransitionToTraitCollection méthode est disponible uniquement sur les contrôleurs d’affichage ( UIViewController) et non sur d’autres environnements de caractéristiques, comme UIViews.

le TraitCollectionDidChange est idéal pour travailler avec la UIView classe, où le développeur souhaite mettre à jour l’interface utilisateur à mesure que les caractéristiques changent.

Réduction des contrôleurs de vue fractionnée

Examinons maintenant de plus près ce qui se passe lorsqu’un contrôleur de vue fractionnée se réduit d’une vue de deux colonnes à une seule colonne. Dans le cadre de cette modification, deux processus doivent se produire :

  • Par défaut, le contrôleur de vue fractionnée utilise le contrôleur d’affichage principal comme vue après la réduction. Le développeur peut remplacer ce comportement en remplaçant la GetPrimaryViewControllerForCollapsingSplitViewController méthode du UISplitViewControllerDelegate et en fournissant n’importe quel contrôleur d’affichage qu’il souhaite afficher dans l’état réduit.
  • Le contrôleur d’affichage secondaire doit être fusionné dans le contrôleur d’affichage principal. En règle générale, le développeur n’a pas besoin d’effectuer d’action pour cette étape ; Le contrôleur de vue fractionnée inclut la gestion automatique de cette phase en fonction de l’appareil matériel. Toutefois, dans certains cas particuliers, le développeur souhaitera interagir avec cette modification. L’appel de la CollapseSecondViewController méthode de permet UISplitViewControllerDelegate au contrôleur de vue master d’être affiché lorsque la réduction se produit, au lieu de la vue détails.

Développement du contrôleur d’affichage partagé

Examinons maintenant de plus près ce qui se passe lorsqu’un contrôleur de vue fractionnée est développé à partir d’un état réduit. Une fois de plus, deux étapes doivent se produire :

  • Tout d’abord, définissez le nouveau contrôleur d’affichage principal. Par défaut, le contrôleur de vue fractionnée utilise automatiquement le contrôleur d’affichage principal à partir de la vue réduite. Là encore, le développeur peut remplacer ce comportement à l’aide de la GetPrimaryViewControllerForExpandingSplitViewController méthode de .UISplitViewControllerDelegate
  • Une fois le contrôleur d’affichage principal choisi, le contrôleur d’affichage secondaire doit être recréé. Là encore, le contrôleur de vue fractionnée inclut la gestion automatique de cette phase en fonction de l’appareil matériel. Le développeur peut remplacer ce comportement en appelant la SeparateSecondaryViewController méthode du UISplitViewControllerDelegate .

Dans un contrôleur de vue fractionnée, le contrôleur d’affichage principal joue un rôle dans le développement et la réduction des vues en implémentant les CollapseSecondViewController méthodes et SeparateSecondaryViewController du UISplitViewControllerDelegate. UINavigationController implémente ces méthodes pour envoyer (push) et afficher automatiquement le contrôleur d’affichage secondaire.

Affichage des contrôleurs

Un autre changement qu’Apple a apporté à iOS 8 est dans la façon dont le développeur affiche les contrôleurs d’affichage. Dans le passé, si l’application avait un contrôleur d’affichage feuille (par exemple, un contrôleur d’affichage table) et que le développeur en affichait un autre (par exemple, en réponse à l’appui de l’utilisateur sur une cellule), l’application revenait à travers la hiérarchie du contrôleur jusqu’au contrôleur de navigation et appelait la PushViewController méthode par rapport à celle-ci pour afficher la nouvelle vue.

Cela a présenté un couplage très étroit entre le contrôleur de navigation et l’environnement dans lequel il s’exécutait. Dans iOS 8, Apple a dissocié cela en fournissant deux nouvelles méthodes :

  • ShowViewController : s’adapte pour afficher le nouveau contrôleur d’affichage en fonction de son environnement. Par exemple, dans un UINavigationController , il pousse simplement la nouvelle vue sur la pile. Dans un contrôleur de vue fractionnée, le nouveau contrôleur d’affichage est présenté sur le côté gauche en tant que nouveau contrôleur de vue principal. Si aucun contrôleur d’affichage conteneur n’est présent, la nouvelle vue s’affiche en tant que contrôleur d’affichage modal.
  • ShowDetailViewController : fonctionne de la même façon que ShowViewController, mais est implémenté sur un contrôleur de vue fractionnée pour remplacer la vue détails par le nouveau contrôleur d’affichage en cours de transmission. Si le contrôleur de vue fractionnée est réduit (comme cela peut être vu dans une application iPhone), l’appel est redirigé vers la ShowViewController méthode et la nouvelle vue s’affiche en tant que contrôleur d’affichage principal. Là encore, si aucun contrôleur d’affichage conteneur n’est présent, la nouvelle vue s’affiche en tant que contrôleur d’affichage modal.

Ces méthodes fonctionnent en commençant par le contrôleur d’affichage feuille et en parcourant la hiérarchie d’affichage jusqu’à ce qu’ils trouvent le contrôleur d’affichage conteneur approprié pour gérer l’affichage de la nouvelle vue.

Les développeurs peuvent implémenter ShowViewController et ShowDetailViewController dans leurs propres contrôleurs d’affichage personnalisés pour obtenir les mêmes fonctionnalités automatisées que UINavigationController et UISplitViewController fournit.

Fonctionnement

Dans cette section, nous allons examiner comment ces méthodes sont réellement implémentées dans iOS 8. Examinons d’abord la nouvelle GetTargetForAction méthode :

Nouvelle méthode GetTargetForAction

Cette méthode guide la chaîne hiérarchique jusqu’à ce que le contrôleur d’affichage de conteneur approprié soit trouvé. Par exemple :

  1. Si une ShowViewController méthode est appelée, le premier contrôleur d’affichage de la chaîne qui implémente cette méthode est le contrôleur de navigation. Il est donc utilisé comme parent de la nouvelle vue.
  2. Si une ShowDetailViewController méthode a été appelée à la place, le contrôleur de vue fractionnée est le premier contrôleur d’affichage à l’implémenter, de sorte qu’il est utilisé comme parent.

La GetTargetForAction méthode fonctionne en localisant un contrôleur d’affichage qui implémente une action donnée, puis en demandant à ce contrôleur d’affichage s’il souhaite recevoir cette action. Étant donné que cette méthode est publique, les développeurs peuvent créer leurs propres méthodes personnalisées qui fonctionnent comme les méthodes intégrées ShowViewController et ShowDetailViewController .

Présentation adaptative

Dans iOS 8, Apple a également rendu les présentations contextuelles ( UIPopoverPresentationController) adaptatives. Ainsi, un contrôleur d’affichage de présentation popover présente automatiquement une vue contextuelle normale dans une classe de taille normale, mais l’affiche en plein écran dans une classe de taille compacte horizontalement (par exemple sur un iPhone).

Pour prendre en charge les modifications dans le système de storyboard unifié, un nouvel objet contrôleur a été créé pour gérer les contrôleurs d’affichage présentés : UIPresentationController. Ce contrôleur est créé à partir du moment où le contrôleur d’affichage est présenté jusqu’à ce qu’il soit ignoré. Comme il s’agit d’une classe de gestion, elle peut être considérée comme une super classe sur le contrôleur d’affichage, car elle répond aux modifications d’appareil qui affectent le contrôleur d’affichage (telles que l’orientation) qui sont ensuite réorientés dans le contrôleur d’affichage que contrôle le contrôleur de présentation.

Lorsque le développeur présente un contrôleur d’affichage à l’aide de la PresentViewController méthode , la gestion du processus de présentation est transmise à UIKit. UIKit gère (entre autres choses) le contrôleur approprié pour le style en cours de création, avec la seule exception quand un contrôleur d’affichage a le style défini sur UIModalPresentationCustom. Ici, l’application peut fournir son propre PresentationController au lieu d’utiliser le UIKit contrôleur.

Styles de présentation personnalisés

Avec un style de présentation personnalisé, les développeurs ont la possibilité d’utiliser un contrôleur de présentation personnalisé. Ce contrôleur personnalisé peut être utilisé pour modifier l’apparence et le comportement de l’affichage à laquelle il est allié.

Utilisation des classes de taille

Le projet Xamarin photos adaptatives inclus dans cet article fournit un exemple pratique d’utilisation de classes de taille et de contrôleurs d’affichage adaptatif dans une application iOS 8 Unified Interface.

Alors que l’application crée entièrement son interface utilisateur à partir du code, au lieu de créer un storyboard unifié à l’aide du générateur d’interface de Xcode, les mêmes techniques s’appliquent.

Examinons maintenant de plus près comment le projet Photos adaptatives implémente plusieurs fonctionnalités de la classe de taille dans iOS 8 pour créer une application adaptative.

Adaptation aux modifications de l’environnement traits

Lors de l’exécution de l’application Photos adaptatives sur un iPhone, lorsque l’utilisateur fait pivoter l’appareil de portrait en mode paysage, le contrôleur d’affichage fractionné affiche à la fois la vue master et les détails :

Le contrôleur de vue fractionnée affiche à la fois la vue master et les détails, comme indiqué ici

Pour ce faire, substituez la UpdateConstraintsForTraitCollection méthode du contrôleur d’affichage et ajustez les contraintes en fonction de la valeur de .VerticalSizeClass Par exemple :

public void UpdateConstraintsForTraitCollection (UITraitCollection collection)
{
    var views = NSDictionary.FromObjectsAndKeys (
        new object[] { TopLayoutGuide, ImageView, NameLabel, ConversationsLabel, PhotosLabel },
        new object[] { "topLayoutGuide", "imageView", "nameLabel", "conversationsLabel", "photosLabel" }
    );

    var newConstraints = new List<NSLayoutConstraint> ();
    if (collection.VerticalSizeClass == UIUserInterfaceSizeClass.Compact) {
        newConstraints.AddRange (NSLayoutConstraint.FromVisualFormat ("|[imageView]-[nameLabel]-|",
            NSLayoutFormatOptions.DirectionLeadingToTrailing, null, views));

        newConstraints.AddRange (NSLayoutConstraint.FromVisualFormat ("[imageView]-[conversationsLabel]-|",
            NSLayoutFormatOptions.DirectionLeadingToTrailing, null, views));

        newConstraints.AddRange (NSLayoutConstraint.FromVisualFormat ("[imageView]-[photosLabel]-|",
            NSLayoutFormatOptions.DirectionLeadingToTrailing, null, views));

        newConstraints.AddRange (NSLayoutConstraint.FromVisualFormat ("V:|[topLayoutGuide]-[nameLabel]-[conversationsLabel]-[photosLabel]",
            NSLayoutFormatOptions.DirectionLeadingToTrailing, null, views));

        newConstraints.AddRange (NSLayoutConstraint.FromVisualFormat ("V:|[topLayoutGuide][imageView]|",
            NSLayoutFormatOptions.DirectionLeadingToTrailing, null, views));

        newConstraints.Add (NSLayoutConstraint.Create (ImageView, NSLayoutAttribute.Width, NSLayoutRelation.Equal,
            View, NSLayoutAttribute.Width, 0.5f, 0.0f));
    } else {
        newConstraints.AddRange (NSLayoutConstraint.FromVisualFormat ("|[imageView]|",
            NSLayoutFormatOptions.DirectionLeadingToTrailing, null, views));

        newConstraints.AddRange (NSLayoutConstraint.FromVisualFormat ("|-[nameLabel]-|",
            NSLayoutFormatOptions.DirectionLeadingToTrailing, null, views));

        newConstraints.AddRange (NSLayoutConstraint.FromVisualFormat ("|-[conversationsLabel]-|",
            NSLayoutFormatOptions.DirectionLeadingToTrailing, null, views));

        newConstraints.AddRange (NSLayoutConstraint.FromVisualFormat ("|-[photosLabel]-|",
            NSLayoutFormatOptions.DirectionLeadingToTrailing, null, views));

        newConstraints.AddRange (NSLayoutConstraint.FromVisualFormat ("V:[topLayoutGuide]-[nameLabel]-[conversationsLabel]-[photosLabel]-20-[imageView]|",
            NSLayoutFormatOptions.DirectionLeadingToTrailing, null, views));
    }

    if (constraints != null)
        View.RemoveConstraints (constraints.ToArray ());

    constraints = newConstraints;
    View.AddConstraints (constraints.ToArray ());
}

Ajout d’animations de transition

Lorsque le contrôleur d’affichage partagé dans l’application Photos adaptatives passe de réduit à développé, les animations sont ajoutées aux animations par défaut en remplaçant la WillTransitionToTraitCollection méthode du contrôleur d’affichage. Par exemple :

public override void WillTransitionToTraitCollection (UITraitCollection traitCollection, IUIViewControllerTransitionCoordinator coordinator)
{
    base.WillTransitionToTraitCollection (traitCollection, coordinator);
    coordinator.AnimateAlongsideTransition ((UIViewControllerTransitionCoordinatorContext) => {
        UpdateConstraintsForTraitCollection (traitCollection);
        View.SetNeedsLayout ();
    }, (UIViewControllerTransitionCoordinatorContext) => {
    });
}

Remplacement de l’environnement traits

Comme indiqué ci-dessus, l’application Photos adaptatives force le contrôleur d’affichage fractionné à afficher à la fois les détails et les vues master lorsque l’appareil iPhone se trouve dans l’affichage paysage.

Pour ce faire, utilisez le code suivant dans le contrôleur d’affichage :

private UITraitCollection forcedTraitCollection = new UITraitCollection ();
...

public UITraitCollection ForcedTraitCollection {
    get {
        return forcedTraitCollection;
    }

    set {
        if (value != forcedTraitCollection) {
            forcedTraitCollection = value;
            UpdateForcedTraitCollection ();
        }
    }
}
...

public override void ViewWillTransitionToSize (SizeF toSize, IUIViewControllerTransitionCoordinator coordinator)
{
    ForcedTraitCollection = toSize.Width > 320.0f ?
         UITraitCollection.FromHorizontalSizeClass (UIUserInterfaceSizeClass.Regular) :
         new UITraitCollection ();

    base.ViewWillTransitionToSize (toSize, coordinator);
}

public void UpdateForcedTraitCollection ()
{
    SetOverrideTraitCollection (forcedTraitCollection, viewController);
}

Développement et réduction du contrôleur d’affichage partagé

Examinons ensuite comment le comportement de développement et de réduction du contrôleur de vue fractionnée a été implémenté dans Xamarin. Dans , AppDelegatelorsque le contrôleur de vue fractionnée est créé, son délégué est affecté pour gérer ces modifications :

public class SplitViewControllerDelegate : UISplitViewControllerDelegate
{
    public override bool CollapseSecondViewController (UISplitViewController splitViewController,
        UIViewController secondaryViewController, UIViewController primaryViewController)
    {
        AAPLPhoto photo = ((CustomViewController)secondaryViewController).Aapl_containedPhoto (null);
        if (photo == null) {
            return true;
        }

        if (primaryViewController.GetType () == typeof(CustomNavigationController)) {
            var viewControllers = new List<UIViewController> ();
            foreach (var controller in ((UINavigationController)primaryViewController).ViewControllers) {
                var type = controller.GetType ();
                MethodInfo method = type.GetMethod ("Aapl_containsPhoto");

                if ((bool)method.Invoke (controller, new object[] { null })) {
                    viewControllers.Add (controller);
                }
            }

            ((UINavigationController)primaryViewController).ViewControllers = viewControllers.ToArray<UIViewController> ();
        }

        return false;
    }

    public override UIViewController SeparateSecondaryViewController (UISplitViewController splitViewController,
        UIViewController primaryViewController)
    {
        if (primaryViewController.GetType () == typeof(CustomNavigationController)) {
            foreach (var controller in ((CustomNavigationController)primaryViewController).ViewControllers) {
                var type = controller.GetType ();
                MethodInfo method = type.GetMethod ("Aapl_containedPhoto");

                if (method.Invoke (controller, new object[] { null }) != null) {
                    return null;
                }
            }
        }

        return new AAPLEmptyViewController ();
    }
}

La SeparateSecondaryViewController méthode teste pour voir si une photo est affichée et prend des mesures en fonction de cet état. Si aucune photo n’est affichée, le contrôleur d’affichage secondaire est réduit afin que le contrôleur d’affichage maître s’affiche.

La CollapseSecondViewController méthode est utilisée lors du développement du contrôleur d’affichage partagé pour voir si des photos existent sur la pile, si c’est le cas, elle se réduit à cette vue.

Déplacement entre les contrôleurs d’affichage

Examinons ensuite comment l’application Photos adaptatives se déplace entre les contrôleurs d’affichage. Dans la AAPLConversationViewController classe lorsque l’utilisateur sélectionne une cellule dans le tableau, la ShowDetailViewController méthode est appelée pour afficher la vue détails :

public override void RowSelected (UITableView tableView, NSIndexPath indexPath)
{
    var photo = PhotoForIndexPath (indexPath);
    var controller = new AAPLPhotoViewController ();
    controller.Photo = photo;

    int photoNumber = indexPath.Row + 1;
    int photoCount = (int)Conversation.Photos.Count;
    controller.Title = string.Format ("{0} of {1}", photoNumber, photoCount);
    ShowDetailViewController (controller, this);
}

Affichage des indicateurs de divulgation

Dans l’application de photo adaptative, il existe plusieurs endroits où les indicateurs de divulgation sont masqués ou affichés en fonction des modifications apportées à l’environnement trait. Cela est géré avec le code suivant :

public bool Aapl_willShowingViewControllerPushWithSender ()
{
    var selector = new Selector ("Aapl_willShowingViewControllerPushWithSender");
    var target = this.GetTargetViewControllerForAction (selector, this);

    if (target != null) {
        var type = target.GetType ();
        MethodInfo method = type.GetMethod ("Aapl_willShowingDetailViewControllerPushWithSender");
        return (bool)method.Invoke (target, new object[] { });
    } else {
        return false;
    }
}

public bool Aapl_willShowingDetailViewControllerPushWithSender ()
{
    var selector = new Selector ("Aapl_willShowingDetailViewControllerPushWithSender");
    var target = this.GetTargetViewControllerForAction (selector, this);

    if (target != null) {
        var type = target.GetType ();
        MethodInfo method = type.GetMethod ("Aapl_willShowingDetailViewControllerPushWithSender");
        return (bool)method.Invoke (target, new object[] { });
    } else {
        return false;
    }
}

Celles-ci sont implémentées à l’aide de la GetTargetViewControllerForAction méthode décrite en détail ci-dessus.

Lorsqu’un contrôleur d’affichage de table affiche des données, il utilise les méthodes implémentées ci-dessus pour voir si un envoi (push) va se produire ou s’il faut afficher ou masquer l’indicateur de divulgation en conséquence :

public override void WillDisplay (UITableView tableView, UITableViewCell cell, NSIndexPath indexPath)
{
    bool pushes = ShouldShowConversationViewForIndexPath (indexPath) ?
         Aapl_willShowingViewControllerPushWithSender () :
         Aapl_willShowingDetailViewControllerPushWithSender ();

    cell.Accessory = pushes ? UITableViewCellAccessory.DisclosureIndicator : UITableViewCellAccessory.None;
    var conversation = ConversationForIndexPath (indexPath);
    cell.TextLabel.Text = conversation.Name;
}

Nouveau ShowDetailTargetDidChangeNotification type

Apple a ajouté un nouveau type de notification pour l’utilisation des classes de taille et des environnements de caractéristiques à partir d’un contrôleur de vue fractionnée, ShowDetailTargetDidChangeNotification. Cette notification est envoyée chaque fois que l’affichage détaillé cible d’un contrôleur d’affichage partagé change, par exemple lorsque le contrôleur se développe ou se réduit.

L’application Photos adaptatives utilise cette notification pour mettre à jour l’état de l’indicateur de divulgation lorsque le contrôleur d’affichage des détails change :

public override void ViewDidLoad ()
{
    base.ViewDidLoad ();
    TableView.RegisterClassForCellReuse (typeof(UITableViewCell), AAPLListTableViewControllerCellIdentifier);
    NSNotificationCenter.DefaultCenter.AddObserver (this, new Selector ("showDetailTargetDidChange:"),
        UIViewController.ShowDetailTargetDidChangeNotification, null);
    ClearsSelectionOnViewWillAppear = false;
}

Examinez de plus près l’application Photos adaptatives pour voir toutes les façons dont les classes de taille, les collections de caractéristiques et les contrôleurs d’affichage adaptatif peuvent être utilisés pour créer facilement une application unifiée dans Xamarin.iOS.

Storyboards unifiés

Nouveauté d’iOS 8, les storyboards unifiés permettent au développeur de créer un fichier de storyboard unifié qui peut être affiché sur les appareils iPhone et iPad en ciblant plusieurs classes de taille. En utilisant des storyboards unifiés, le développeur écrit moins de code spécifique à l’interface utilisateur et n’a qu’une seule conception d’interface à créer et à gérer.

Les principaux avantages des storyboards unifiés sont les suivants :

  • Utilisez le même fichier de storyboard pour iPhone et iPad.
  • Déployer vers l’arrière sur iOS 6 et iOS 7.
  • Affichez un aperçu de la disposition des différents appareils, orientations et versions du système d’exploitation .

Activation des classes de taille

Par défaut, tout nouveau projet Xamarin.iOS utilise des classes de taille. Pour utiliser des classes de taille et des valeurs adaptatives à l’intérieur d’une table de montage séquentiel à partir d’un projet plus ancien, vous devez d’abord les convertir au format De storyboard unifié Xcode 6 et la case Utiliser les classes de taille est cochée dans l’inspecteur de fichiers Xcode pour vos storyboards.

Écrans de lancement dynamique

Le fichier d’écran de lancement s’affiche sous la forme d’un écran de démarrage pendant le lancement d’une application iOS pour indiquer à l’utilisateur que l’application est en cours de démarrage. Avant iOS 8, le développeur devait inclure plusieurs Default.png ressources d’image pour chaque type d’appareil, orientation et résolution d’écran sur lequel l’application s’exécutait. Par exemple, Default@2x.png, Default-Landscape@2x~ipad.png, Default-Portrait@2x~ipad.png, etc.

Si l’on tient compte des nouveaux appareils iPhone 6 et iPhone 6 Plus (et de l’Apple Watch à venir) avec tous les appareils iPhone et iPad existants, cela représente un large éventail de tailles, d’orientations et de résolutions de ressources d’image d’écran de Default.png démarrage qui doivent être créées et gérées. En outre, ces fichiers peuvent être assez volumineux et « gonfler » le bundle d’applications livrables, ce qui augmente le temps nécessaire pour télécharger l’application à partir de l’App Store iTunes (éventuellement l’empêchant de pouvoir être livré sur un réseau cellulaire) et augmente la quantité de stockage requise sur l’appareil de l’utilisateur final.

Nouveau dans iOS 8, le développeur peut créer un fichier atomique .xib unique dans Xcode qui utilise des classes de disposition et de taille automatiques pour créer un écran de lancement dynamique qui fonctionnera pour chaque appareil, résolution et orientation. Cela réduit non seulement la quantité de travail nécessaire au développeur pour créer et gérer toutes les ressources d’image requises, mais cela réduit considérablement la taille du bundle installé de l’application.

Les écrans de lancement dynamique présentent les limitations et considérations suivantes :

  • Utilisez uniquement UIKit des classes.
  • Utilisez une vue racine unique qui est un UIView objet ou UIViewController .
  • N’effectuez aucune connexion au code de l’application (n’ajoutez pas d’actions ou de sorties).
  • N’ajoutez UIWebView pas d’objets.
  • N’utilisez pas de classes personnalisées.
  • N’utilisez pas d’attributs d’exécution.

En tenant compte des instructions ci-dessus, examinons l’ajout d’un écran de lancement dynamique à un projet Xamarin iOS 8 existant.

Effectuez les actions suivantes :

  1. Ouvrez Visual Studio pour Mac et chargez la solution à laquelle ajouter l’écran de lancement dynamique.

  2. Dans le Explorateur de solutions, cliquez avec le bouton droit sur le MainStoryboard.storyboard fichier et sélectionnez Ouvrir avec>Xcode Interface Builder :

    Ouvrir avec Xcode Interface Builder

  3. Dans Xcode, sélectionnez Fichier>Nouveau>fichier... :

    Sélectionner Fichier / Nouveau

  4. Sélectionnez Écran delancementde l’interface> utilisateur iOS>, puis cliquez sur le bouton Suivant :

    Sélectionner iOS / Interface utilisateur / Écran de lancement

  5. Nommez le fichier LaunchScreen.xib et cliquez sur le bouton Créer :

    Nommez le fichier LaunchScreen.xib

  6. Modifiez la conception de l’écran de lancement en ajoutant des éléments graphiques et en utilisant des contraintes de disposition pour les positionner pour les appareils, orientations et tailles d’écran donnés :

    Modification de la conception de l’écran de lancement

  7. Enregistrez les changements apportés à LaunchScreen.xib.

  8. Sélectionnez la cible des applications et l’onglet Général :

    Sélectionnez la cible des applications et l’onglet Général

  9. Cliquez sur le bouton Choisir Info.plist , sélectionnez le Info.plist pour l’application Xamarin, puis cliquez sur le bouton Choisir :

    Sélectionnez info.plist pour l’application Xamarin

  10. Dans la section Icônes d’application et Images de lancement, ouvrez la liste déroulante Fichier d’écran de lancement et choisissez le LaunchScreen.xib créé ci-dessus :

    Choisissez LaunchScreen.xib

  11. Enregistrez les modifications apportées au fichier et revenez à Visual Studio pour Mac.

  12. Attendez que Visual Studio pour Mac fin de la synchronisation des modifications avec Xcode.

  13. Dans le Explorateur de solutions, cliquez avec le bouton droit sur le dossier Ressource et sélectionnez Ajouter>des fichiers... :

    Sélectionnez Ajouter/Ajouter des fichiers...

  14. Sélectionnez le LaunchScreen.xib fichier créé ci-dessus, puis cliquez sur le bouton Ouvrir :

    Sélectionnez le fichier LaunchScreen.xib

  15. Générez l’application.

Test de l’écran de lancement dynamique

Dans Visual Studio pour Mac, sélectionnez le simulateur iPhone 4 Retina et exécutez l’application. L’écran de lancement dynamique s’affiche dans le format et l’orientation appropriés :

Écran de lancement dynamique affiché dans l’orientation verticale

Arrêtez l’application dans Visual Studio pour Mac et sélectionnez un appareil iPad iOS 8. Exécutez l’application et l’écran de lancement sera correctement mis en forme pour cet appareil et cette orientation :

Écran de lancement dynamique affiché dans l’orientation horizontale

Revenez à Visual Studio pour Mac et arrêtez l’exécution de l’application.

Utilisation d’iOS 7

Pour maintenir la compatibilité descendante avec iOS 7, incluez simplement les ressources d’image habituelles Default.png comme d’habitude dans l’application iOS 8. iOS revient au comportement précédent et utilise ces fichiers comme écran de démarrage lors de l’exécution sur un appareil iOS 7.

Pour voir une implémentation d’un écran de lancement dynamique dans Xamarin, consultez l’exemple d’application iOS 8 Écrans de lancement dynamiques joint à ce document.

Résumé

Cet article a examiné rapidement les classes de taille et leur impact sur la disposition des appareils iPhone et iPad. Il a expliqué comment traits, environnements de caractéristiques et collections de caractéristiques fonctionnent avec les classes de taille pour créer des interfaces unifiées. Il a fallu examiner brièvement les contrôleurs d’affichage adaptatif et comment ils fonctionnent avec les classes de taille à l’intérieur d’interfaces unifiées. Il a examiné l’implémentation complète des classes de taille et des interfaces unifiées à partir du code C# dans une application Xamarin iOS 8.

Enfin, cet article a abordé les principes de base de la création d’un écran de lancement dynamique unique qui sera affiché en tant qu’écran de démarrage sur chaque appareil iOS 8.