• 10 heures
  • Difficile

Ce cours est visible gratuitement en ligne.

course.header.alt.is_video

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 05/04/2023

Appliquez le delegate pattern

Notre TableView est bien installée, mais, pour l'instant, elle est complètement vide. Cela ne va pas durer ! Dans ce chapitre, je vais vous présenter le mécanisme qui va nous permettre de la remplir : le delegate pattern !

Présentation du delegate pattern

Commençons par faire un retour sur le MVC :

Au milieu, le Contrôleur. Une flèche Propriété va vers Modèle, un Callback revient vers Contrôleur. Une flèche Outlet part à gauche vers la vue. Une flèche part de la vue et target le Contrôleur
Il vous avait manqué ?

Avec les outlets, le contrôleur peut donner à la vue les données dont elle a besoin pour s'afficher. Cette méthode fonctionne bien, mais a ses limites. Elle ne marche que pour de petites quantités de données.

Or, comme on l'a vu, une liste peut avoir des tonnes de données ! On ne va pas tout donner à la vue en lui demandant de se débrouiller avec. La vue ne doit détenir que les données qui lui permettent de faire l'affichage, car c'est son seul rôle.

En conséquence, la vue va devoir demander régulièrement au contrôleur de lui donner de nouvelles données. À chaque fois que l'on fait défiler la vue, elle va réclamer de nouvelles données au contrôleur.

Le problème, c'est que ma TableView ne sait pas avec quel contrôleur elle va travailler. Ici, nous utilisons notre  ListViewController  qui présente une liste de jouets, mais ailleurs, nous pourrions en utiliser un autre qui fournit des listes de réglages, ou des listes de contacts...

Pour faire fonctionner notre TableView, on doit donc résoudre le problème suivant :

  1. Ma TableView doit pouvoir être informée de la composition de la liste par le contrôleur.

  2. N'importe quel objet doit pouvoir faire ce travail. Ma TableView se moque de savoir avec qui elle travaille, et cela me permet de la réutiliser dans de multiples situations.

Pour résoudre ce double problème, on va utiliser le delegate pattern. On dit en effet que la TableView délègue une partie de son fonctionnement à un autre objet. Et au cœur du delegate pattern, on va voir ce que nous avons étudié dans la partie 1 : un protocole !

Le delegate pattern est une nouvelle méthode de communication aveugle entre la vue et le contrôleur.

Et voici comment cela fonctionne :

  1. On crée une liste de questions que la vue peut poser.

  2. La vue nomme un objet son delegate, en l'occurrence notre contrôleur.

  3. Le contrôleur s'engage à répondre aux questions sur la liste.

  4. Le contrôleur répond effectivement aux questions.

Delegate pattern et protocoles

Détaillons maintenant ces 4 étapes en rentrant dans le détail du protocole.

1. On crée une liste de questions que la vue peut poser

La liste de questions est une liste d'exigences, en fait. Donc ici, on parle de la création d'un protocole. Pour  UITableView  , ce protocole se nomme  UITableViewDataSource  , car c'est la source de données.

protocol UITableViewDataSource: NSObjectProtocol {

   func numberOfSections(in tableView: UITableView) -> Int

   func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int

   func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell

   // (...)

}

2. La vue nomme un objet son delegate, en l'occurrence notre contrôleur

UITableView  a une propriété  dataSource  de type  UITableViewDataSource  :

class UITableView: UIScrollView {

   weak var dataSource: UITableViewDataSource?

}

Cette propriété est utilisée à l'intérieur de la classe  UITableView  pour appeler les méthodes du protocole au moment où la TableView en a besoin. Comme promis, notre TableView se fiche bien de savoir quel objet fera office de dataSource. Elle a juste besoin de savoir qu'il répondra aux exigences de notre protocole.

Ensuite, cette propriété prend pour valeur le contrôleur :

class ViewController: UIViewController {

   var tableView: UITableView

   override func viewDidLoad() {

      super.viewDidLoad()

      // J'assigne le contrôleur comme valeur de la propriété dataSource.

      tableView.dataSource = self

   }

}

3. Le contrôleur s'engage à répondre aux questions sur la liste

Le contrôleur va ici adopter le protocole  UITableViewDataSource  :

extension ViewController: UITableViewDataSource {

}

4. Le contrôleur répond effectivement aux questions

Le contrôleur implémente les méthodes de  UITableViewDataSource  :

extension ViewController: UITableViewDataSource {
   func numberOfSections(in tableView: UITableView) -> Int {
      return 1
   }

   func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
      return 10
   }

   func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
      // (...)
   }
}

Vous venez de découvrir un nouveau mode de communication aveugle entre la vue et le contrôleur :

Le même MVC avec une datasource implémentée sur le contrôleur
Communication aveugle Vue / Contrôleur

Mise en place de la dataSource !

Mettons toute cette théorie en application dans notre code.

2. La vue nomme un objet son delegate, en l'occurrence notre contrôleur

Pour que la TableView nomme le contrôleur sa dataSource, on peut faire de notre TableView un outlet de ListViewController, et ensuite recopier le code que je vous ai donné au-dessus.

Mais nous allons faire encore plus simple en utilisant le control drag !

Faites un control drag dans le storyboard depuis la TableView vers le contrôleur :

Un drag part de la Table View jusqu'à Outlets puis clic sur DataSource
Et voilà, vous venez de nommer votre contrôleur, la dataSource de la TableView.

Et voilà, vous venez de nommer votre contrôleur, la dataSource de la TableView.

3. Le contrôleur s'engage à répondre aux questions sur la liste

Comme on l'a vu à cette étape, nous allons faire adopter le protocole  UITableViewDataSource  à notre contrôleur  ListViewController  en passant par une extension :

extension ListViewController:UITableViewDataSource {}

4. Le contrôleur répond effectivement aux questions

Ensuite, nous allons implémenter les méthodes du protocole pour nous y conformer correctement. Nous n'allons en implémenter que trois, car les autres sont optionnelles.

@objc protocol MonProtocole {

   optional func maMethodeOptionnelle()

   func maMethodeRequise()

}

La première méthode que nous allons implémenter s'appelle  numbersOfSection  :

extension ListViewController: UITableViewDataSource {

   func numberOfSections(in tableView: UITableView) -> Int {

      return 1

   }

}

Cette méthode permet de préciser à la TableView le nombre de sections dont nous allons avoir besoin. Dans notre cas, nous ne voulons pas séparer notre code selon des sections, donc nous allons simplement renvoyer 1 pour avoir une seule section.

La deuxième méthode se nomme  numbersOfRowsInSection  :

extension ListViewController: UITableViewDataSource {

   // (...)

   func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

      return ToyService.shared.toys.count

   }

}

Elle permet de préciser le nombre de cellules que l'on veut pour chaque section. Dans notre cas, on veut autant de cellules que nous avons d'éléments dans notre tableau de jouets  toys  .

La troisième méthode va nous permettre de préciser le contenu de chaque cellule. Je vous propose que l'on voie cela ensemble dans le prochain chapitre !

En résumé

  • La delegate pattern est une communication aveugle entre la vue et le contrôleur. Elle s'organise en 4 étapes :

  1. On crée une liste de questions que la vue peut poser.

  2. La vue nomme un objet son delegate, en l'occurrence notre contrôleur.

  3. Le contrôleur s'engage à répondre aux questions sur la liste.

  4. Le contrôleur répond effectivement aux questions.

  • La liste de questions est en fait un protocole auquel se conforme le contrôleur.

  • Dans l'étape 2, on peut utiliser directement le control drag pour nommer le contrôleur le  dataSource  .

Dans le prochain chapitre, nous allons remplir notre TableView en précisant le contenu de nos cellules !

Exemple de certificat de réussite
Exemple de certificat de réussite