Chers amis passionnés du swift, aujourd’hui est un grand jour. Aujourd’hui enfin nous allons utiliser notre couteau affuté de la connaissance et du savoir pour découper la viande tendre et fraiche de l’expérimentation et de la pratique. Aujourd’hui, nous allons découvrir le fonctionnement des contrôleurs de vues de base proposés par notre cher UIKit qui nous permettront de créer des applications. C’est parti pour la mise en application de tout ce que l’on a vu auparavant !
Une vue que vous avez déjà vu !
Les UITabBarController sont la base de nombreuses applications iOS, par exemple l’app store. Il s’agit d’un contrôleur de vue pré-fabriqué par Apple qui va nous permettre en un rien de temps d’y lier d’autres contrôleurs et permettre via l’UI de switcher de l’un à l’autre avec une grande facilité, tout en maintenant un interface unifiée entre toutes les apps. Concrètement il s’agit du contrôleur qui va englober les autres et nous afficher une barre d’onglet en bas pour y accéder. Voyons cela d’un peu plus près !
Ouvrons Xcode et créons un nouveau projet d’app iOS. Une fois le projet créé et initialisé, vous trouvez le fichier Main.storyboard que vous connaissez déjà et qui contient un contrôleur de vue simple. Nous n’en aurons pas besoin alors supprimons-le pour obtenir une planche vide. Nous allons pouvoir ouvrir notre bibliothèque d’objets pour y rechercher le Tab Bar Controller qui nous intéresse, et le déposer sur notre planche de visualisation du storyboard.
On y voit inclus 3 contrôleurs de vue :
- Tab Bar Controller : le contrôleur principal qui va générer la barre d’onglets en bas de l’écran, appelés généralement contrôleur parent
- Item 1, Item 2 : les contrôleurs qui seront utilisés comme onglets, appelés généralement contrôleurs enfants
Notons que c’était appellation n’est pas spécifique aux tab bar controllers mais s’applique à toutes les hiérarchies des contrôleurs de vues. Auparavant on appelait cette relation master / slave (maître / esclave) il est donc possible que vous trouviez toujours cette nomenclature sur le web, mais on préfère l’éviter ici.
Plutôt simple, vu d’ici. On voit deux flèches qui pointent du contrôleur principal vers les contrôleurs enfants qui indiquent le lien de parenté entre eux. Cette relation est visible dans la vue en liste sur la gauche sous la Tab View Controller Scene sur les deux lignes qui commencent par « Relationship ». On remarque aussi sur la planche de l’interface builder que le tab bar controller à un fond gris plutôt que blanc comme les autres, c’est parce qu’il n’est pas possible d’y ajouter directement des vues, il ne pourra servir que de gestionnaire pour d’autres contrôleurs.
Essayons donc d’ajouter un peu de texte dans nos contrôleurs enfants pour essayer un peu d’exécuter ce code, simplement un champ de texte par contrôleur avec un texte différent bien sûr pour pouvoir les différencier. Avant de lancer l’exécution, il va juste falloir réparer un peu notre app. En effet, en supprimant le contrôleur de vue initial (celui présent à la création du projet), nous avons aussi supprimé le point d’entrée de notre storyboard, ce qui fait qu’une fois chargé, notre app ne saura pas quel contrôleur charger et afficher en premier. Pour corriger cela, il va falloir suivre quelques étapes :
- Sélectionner le contrôleur de vue parent qui deviendra le point d’entrée
- Dans l’inspecteur, sélectionner l’inspecteur d’attributs ()
- Dans la section View Controller, cochez la case Is initial View Controller
- Vérifiez que le Tab Bar Controller possède maintenant Storyboard Entry Point dans sa vue hiérarchique. On voit aussi un flèche pointer vers le contrôleur sur la planche
Une fois cela effectué, il est temps pour nous de lancer la compilation et l’exécution de notre app pour admirer le résultat. On voit bien nos onglets, et en cliquant dessus on voit notre vue changer d’un contrôleur enfant à l’autre :
Il nous est possible d’ajouter de nouveaux onglet assez facilement :
- Tout d’abord nous ajoutons un nouveau contrôleur de vue depuis la bibliothèque d’objets
- On sélectionne notre Tab View Controller
- On ouvre l’inspecteur de connexions ()
- On retrouve notre propriété View Controllers qui liste déjà nos deux onglets, il nous suffit de cliquer sur le bouton (+) et de glisser jusqu’à notre contrôleur
Personnalisons un peut tout ça
Bon c’est pas mal, mais le fait qu’il y ait des onglets ce n’est pas encore vraiment très évidement lors de l’exécution. Nous voyons bien un carré bleu dans l’interface builder mais juste un texte un peu timide dans l’aperçu. Nous allons donc nous pencher un peu sur la personnalisation. Sur chaque contrôleur enfant, on remarque qu’un onglet est présent en bas, il représente l’onglet qui sera affiché dans la barre d’onglets du contrôleur parent. Si l’on clique dessus, on voit dans l’inspecteur d’attributs plusieurs réglages intéressants.
On voit notamment qu’ils sont séparé en deux catégories :
- Tab bar item : réglages de l’item lorsqu’il est actif, lorsque l’onglet est actif
- Bar item : réglages de l’item quand il n’est pas actif, c’est à dire lorsqu’un autre onglet est sélectionné
Pas besoin de systématiquement remplir les deux d’ailleurs, si par exemple une image est sélectionnée dans tab bar item et pas dans dans bar item, il y aura quand même une image d’affichée que l’onglet soit sélectionné ou non.
Qu’est-ce que ça donne niveau code ?
Oui parce que l’interface builder c’est bien sympa mais qu’en est-il du code ? Après tout nous sommes ici pour apprendre le Swift. Alors il y a deux écoles, ceux qui font de l’interface builder et ceux qui n’en font pas. Tout ce qui est réalisable via cet outil l’est également en Swift (plus ou moins facilement) mais tout ce qui est réalisable avec le code ne l’est pas forcément dans un storyboard. Je vais faire de mon mieux pour vous donner les deux versions pour que vous ayez la vision la plus d’ensemble possible. Ce que l’on vient de faire là est le strict équivalent de ce code là :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | import UIKit class ViewController: UITabBarController { override func viewDidLoad() { super.viewDidLoad() let vc1 = UIViewController() vc1.view.backgroundColor = UIColor.white vc1.tabBarItem = UITabBarItem(title: "Item 1", image: nil, tag: 0) let vc2 = UIViewController() vc2.view.backgroundColor = UIColor.white vc2.tabBarItem = UITabBarItem(title: "Item 2", image: nil, tag: 1) viewControllers = [vc1, vc2] } } |
La seule chose qui ne sera pas gérée pour l’instant est le point d’entrée de l’application, on se contentera de conserver le contrôleur de vue généré automatiquement par le projet et de l’associer à la classe ViewController qui est décrite dans ce code
Examinons un peu le code de cette classe ViewController, tout d’abord on lui définit l’héritage de la classe qui nous intéresse : UITabBarController. Ensuite, ma foi c’est assez simple pour le coup, on crée des nouveaux contrôleurs de vue (ici juste vides), on précise que leur couleur de fond sera blanche (par défaut elle est noire) et on leu assigne à chacun d’eux un UITabBarItem dans leur propriété .tabBarItem
. Cette propriété est accessible pour tout type de contrôleur de vue, elle ne sera en revanche utilisée que lorsque ledit contrôleur est lui-même enfant d’un UITabBarController. Une fois nos contrôleurs instanciés, il nous suffit de les ranger dans un tableau les listant dans l’ordre.
Il est également possible d’appeler la fonction func setViewControllers(_ viewControllers: [UIViewController]?, animated: Bool)
pour associer nos contrôleurs de vue, on va généralement l’appeler dès que l’on souhaite modifier notre liste de contrôleurs une fois l’exécution lancée afin qu’UIKit puisse recalculer la disposition et re-paramétrer les contrôleurs proprement.
Que se passe-t-il avec trop de contrôleurs ?
En effet comme vous avez pu le constater le contrôleur d’onglet ne peut contenir qu’un nombre limité d’onglets différents, en l’occurence 5 sur un iPhone. Par conséquent si l’on ajoute 6 contrôleurs enfants, UIKit va nous remplacer le dernier onglet par un bouton « More » qui va permettre d’avoir accès aux autres onglets masqués, et même de pouvoir les modifier.
Dans cet exemple je n’ai fait qu’ajouter plusieurs fois le même contrôleur ce qui fait que tous les onglets sont identiques, c’est uniquement pour la démonstration
A gauche on voit les onglets sous forme de liste qui n’ont pas eu la place d’être affiché, on peut donc accéder à leur contenu de cette façon, à droite il y a la vue d’édition qui permet de modifier les onglets que l’on souhaite dans la barre, leur ordre, etc… il suffit de les glisser-déposer. Notons tout de même que tout ceci s’est fait automatiquement sans que l’on ai eu à coder quoique ce soit en plus, l’ordre des onglets se sauvegarde même automatiquement sur l’appareil si il a été modifié !
Définir un délégué : UITabBarControllerDelegate
Comme énormément de classe avec UIKit, il est possible de définir un délégué à notre contrôleur qui permettra de réguler certains aspects de ce dernier notamment liés aux interactions avec l’utilisateur. Pour faire simple, un délégué est une classe qui va recevoir certains évènements envoyés par le contrôleur et qui doit se conformer à un certain protocole, en l’occurence : UITabBarControllerDelegate. Pour le définir, il suffit de définir la variable .delegate
de notre contrôleur et de lui passer la classe de notre choix, pour faire simple nous allons prendre notre contrôleur lui-même :
1 2 3 4 5 6 7 8 9 | class ViewController: UITabBarController, UITabBarControllerDelegate { override func viewDidLoad() { super.viewDidLoad() self.delegate = self } } |
Il n’est pas possible de faire conformer n’importe quelle classe à ce protocole, elle doit déjà se conformer à NSObjectProtocol afin de pouvoir se conformer à UITabBarControllerDelegate. La plupart du temps, le délégué sera défini sur le contrôleur lui-même, ou sur un contrôleur de vue parent.
Cela va nous permettre d’ajouter des fonctions à notre classe, en l’occurence toutes optionnelles, qui seront appelées lors de différents évènements, par exemple pour savoir si il est possible de sélectionner ou pas un onglet :
1 2 3 4 5 6 7 8 9 10 11 12 13 | class ViewController: UITabBarController, UITabBarControllerDelegate { override func viewDidLoad() { super.viewDidLoad() self.delegate = self } func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool { return true } } |
Cette méthode a une déclaration assez particulière car son nom n’est pas directement relié à sa fonction, c’est plutôt ses attributs qui définiront son utilité. C’est le cas car il est bien sur possible qu’un seul et même contrôleur se conforme à plusieurs protocoles de délégué de différents classes et souvent plusieurs méthodes auront la même dénomination, comme ça on ne les mélange pas trop. En premier attribut, on retrouve le contrôleur lui-même, cela permet d’y accéder si le délégué est définit sur une autre classe, ou si plusieurs UITabBarController on définit leur délégué sur une seule et même classe, ce qui est à éviter de préférence !
En deuxième attribut, on trouve la véritable dénomination de la fonction de la méthode : shouldSelect: UIViewController
. Dans cette fonction donc on retournera un booléen qui autorisera ou pas la sélection d’un certain contrôleur de vue par un onglet. Mais ce n’est pas la seule méthode possible d’ajouter, voyons une liste rapide :
func tabBarController(UITabBarController, shouldSelect: UIViewController) -> Bool
on vient de le voir, appelée pour savoir si un certain contrôleur de vue peut être sélectionné ou pasfunc tabBarController(UITabBarController, didSelect: UIViewController)
appelée une fois qu’un contrôleur de vue a été sélectionné, rien à retourner, c’est plutôt comme une notificationfunc tabBarController(UITabBarController, willBeginCustomizing: [UIViewController])
appelée lorsque le contrôleur de personnalisation va être affichéfunc tabBarController(UITabBarController, willEndCustomizing: [UIViewController], changed: Bool)
appelée lorsque le contrôleur de personnalisation va être ferméfunc tabBarController(UITabBarController, didEndCustomizing: [UIViewController], changed: Bool)
appelée lorsque le contrôleur de personnalisation vient d’être fermé
Il en existe quelques autres plus complexes retournant des objets que l’on a pas encore vu pour aller très loins dans la personnalisation et les animations du contrôleur, je vous renvoie à la documentation générale si cela vous intéresse : https://developer.apple.com/documentation/uikit/uitabbarcontrollerdelegate.
Tout ceci me semble être un bon début, il est difficile de savoir quand s’arrêter car il existe une infinité de subtilités et de petits détails qui valent tous le coup d’être étudiés mais on va se garder tout ça pour une autre fois, je pense qu’avec ça vous avez une bonne base. On se retrouvera prochainement pour découvrir d’autres types de contrôleurs préfabriqués et souvent bien pratiques, en attendant je vous dis à tous bon code et à bientôt !