• 8 heures
  • Moyenne

Ce cours est visible gratuitement en ligne.

course.header.alt.is_video

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 26/05/2023

Remplissez le sélecteur

Dans le chapitre précédent, nous avons créé l'interface de notre application, mais malheureusement le sélecteur a échappé à notre folie artistique ! Nous n'avons pas pu le configurer dans le storyboard. Et du coup, quand vous lancez l'application, le résultat en la matière n'est pas probant...

L'interface d'inscription complet… mais il manque le sélecteur

Qu'à cela ne tienne, nous aurons sa peau dans le code !

Delegate, dataSource et MVC

En MVC, vous savez qu'il existe plusieurs formes de communication entre les trois parties. Ce schéma doit vous rappeler quelque chose :

Présentation graphique du MVC
Retour au MVC

Notamment, on y voit que la vue communique avec le contrôleur via les outlets et les actions. Nous allons découvrir ici deux nouvelles formes de communication : les dataSources et les delegates.

La dataSource

En MVC, une vue ne détient pas ses données. Car une vue n'a qu'un rôle d'affichage, ce n'est pas son rôle de savoir quoi afficher. C'est le contrôleur qui va le lui dire.

Ainsi, dans le cas de notre liste de races, ce n'est pas la vue qui doit retenir la liste de races, c'est le contrôleur qui va lui dire quoi afficher. Et il va tirer cette information du modèle qui détient les données.

Donc pour savoir quoi afficher, le contrôleur va simplement utiliser des outlets pour les vues relativement simples et surtout qui ont peu de données à afficher. Mais pour des vues plus complexes avec beaucoup de données à afficher, le contrôleur va se positionner en source de données pour que la vue puisse piocher dedans, et donc n'avoir en même temps que très peu de données à afficher.

Le contrôleur est donc la  dataSource  ,  celle qui détient les données. Et voilà comment cela va fonctionner :

  1. Le PickerView va nommer le contrôleur source de données.

  2. Cela implique que le contrôleur s'engage à répondre à certaines questions précises du PickerView ; par exemple, combien d'éléments y a-t-il dans la liste ?

  3. Lorsque la vue en a besoin, le contrôleur répond aux questions.

Vous venez de découvrir une nouvelle forme de communication aveugle entre le contrôleur et la vue.

Le delegate

Le delegate fonctionne de la même manière. Une vue peut avoir besoin de poser des questions à son contrôleur selon les évènements qui lui arrivent.

Mais parfois, on ne veut pas toujours faire la même action. Par exemple, lorsque l'utilisateur va faire défiler la liste des races, la vue va devoir demander à chaque fois : quel est le prochain nom de race que je dois afficher ? Et à chaque fois, le contrôleur va répondre une race différente. C'est lui qui choisit ce qu'on affiche.

Pour prendre un autre exemple, on peut parler des  scroll view  . Une  scroll view  va utiliser le mécanisme de delegate pour dire au contrôleur :

  • Je viens de scroller de quelques pixels, tu veux faire une action ?

  • Je viens d'atteindre la fin du scroll, on fait quelque chose ?

  • Je viens de m'arrêter de scroller, que veux-tu faire ?

Autrement dit, la vue délègue toute prise de décision au contrôleur. D'où le nom de ce mécanisme.

Le contrôleur devient ce qu'on appelle le delegate, celui à qui est déléguée la responsabilité. Et cela va fonctionner exactement comme pour la data source :

  1. Le PickerView va nommer le contrôleur délégué.

  2. Cela implique que le contrôleur s'engage à répondre à certaines questions précises du PickerView.

  3. Lorsque la vue en a besoin, le contrôleur répond aux questions.

Le MVC avec la data source et le delegate reliés à la Vue
DataSource et Delegate dans le MVC

Encore une nouvelle forme de communication aveugle entre le contrôleur et la vue. Vos compétences en MVC ont fait un bond en avant !

Ces 3 étapes vont être les trois étapes que nous allons suivre pour configurer le delegate et la dataSource de notre PickerView.

Nommez la dataSource et le delegate

Lors de la première étape, la vue nomme le contrôleur sa dataSource et son delegate. Et nous allons faire cela avec... deux control-drags !

Il vous suffit de faire le control-drag depuis le Picker View vers le contrôleur, comme ceci :

Gif : Un drag partant du pickerview jusqu'à Outlets, option dataSource
Control-drag du Picker View au contrôleur

Une pop-up apparaît. Cliquez sur  dataSource  , puis répétez l'opération et cliquez cette fois sur  delegate  .

Le contrôleur s'engage à répondre aux questions

Deuxième étape : le contrôleur s'engage à répondre aux questions de la vue. Pour cela, le contrôleur va implémenter ce qu'on appelle un protocole.

Un protocole, c'est un outil en Swift dont nous n'avons pas encore parlé. Un protocole est une liste de méthodes vides et de propriétés.

De méthodes vides ?

Oui, un protocole, c'est juste une liste de méthodes vides. Contrairement à une classe, dans laquelle on écrit une méthode et son implémentation, dans un protocole on n'a que la déclaration des fonctions.

Pour vous donner un exemple, voici le protocole  UIPickerViewDataSource  .

protocol UIPickerViewDataSource {

 

   func numberOfComponents(in pickerView: UIPickerView) -> Int

 

   func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int

 

}

C'est juste une liste de méthodes.

À quoi ça sert ?

Justement, ça va nous être très utile ici. On veut que le contrôleur s'engage à répondre à des questions. Et les questions, ce sont simplement les méthodes du protocole !

Pour cela, notre contrôleur va se conformer au protocole. C'est un terme technique pour dire qu'il s'engage à fournir une implémentation aux méthodes qui se trouvent dans le protocole.

Pour concrétiser cet engagement, il suffit d'ajouter une extension à notre classe, et d’ajouter la conformance à ce protocole via l’extension, comme ceci :

extension FormViewController: UIPickerViewDataSource {

}

extension FormViewController: UIPickerViewDelegate {

}

Une fois que vous avez écrit cela, vous devriez voir des erreurs s'afficher.

Type 'FormViewController' does not conform to protocol 'UIPickerViewDataSource'

Le problème, c'est qu'on s'engage à implémenter des méthodes, mais on ne le fait pas ! Xcode n'est pas content !

Résolvons cela.

Le contrôleur répond aux questions

Il ne nous reste plus qu'à répondre aux questions. Et pour cela, nous allons implémenter les méthodes de nos deux protocoles :  UIPickerViewDataSource  et  UIPickerViewDelegate  .

Méthodes de la dataSource

La première, c'est donc :

func numberOfComponents(in pickerView: UIPickerView) -> Int {

}

Ajoutez cette fonction dans le code du  FormViewController  . Nous allons l'implémenter. Cette méthode doit apparemment renvoyer un entier. Cet entier correspond au numberOfComponents de notre PickerView.

C'est quoi, les components du PickerView ?

Les components représentent les différentes colonnes de notre PickerView. Souvenez-vous, le PickerView fonctionne comme la roulette au casino. Par exemple, sur la roulette suivante, il y a quatre components.

Un pickerview, qui se présente comme une roulette, ici sous la forme d'un calendrier avec jours de la semaine et heures
Exemple de PickerView

Le nôtre est bien plus simple puisqu'on veut ajouter seulement une liste, celle des races de chiens.

Donc on va répondre simplement à la question en implémentant la méthode comme ceci :

func numberOfComponents(in pickerView: UIPickerView) -> Int {

   return 1

}

Maintenant, la vue sait combien de components afficher.

Passons maintenant à la deuxième méthode :

func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {

}

Cette méthode retourne également un entier. Cette fois, il correspond au nombre d'éléments présents dans chaque colonne. C'est la raison pour laquelle nous avons component en paramètre, pour pouvoir adapter la réponse en fonction de la colonne.

Nous, nous n'avons qu'une seule colonne, donc on va simplement répondre par le nombre de races de chiens à afficher. Et pour cela, nous allons tout simplement utiliser le tableau dogRaces qui se trouve dans notre modèle.

func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {

   return dogRaces.count

}

Et voilà ! La vue connaît maintenant le nombre d'éléments à prévoir dans la liste.

Méthode du delegate

UIPickerViewDelegate  est un protocole qui contient bien plus de méthodes qui permettent de personnaliser complètement la vue. Mais il y en a une seule qui nous intéresse vraiment, c'est celle-ci, que je vous invite à rajouter dans votre code :

func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {

}

Cette méthode renvoie une string optionnelle qui correspond au titre que l'on veut mettre pour chaque élément. Évidemment, ce titre dépend de la colonne et de la ligne dont on parle. C'est la raison pour laquelle nous avons les entiers  row  et  component  en paramètres de cette méthode.

Encore une fois, le paramètre  component  ne nous importe pas puisque nous n'avons qu'une seule colonne. En revanche, on va utiliser le paramètre  row  en index de notre tableau  dogRaces  .

Vous allez donc implémenter la méthode comme ceci :

func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {

   return dogRaces[row]

}

Et voilà ! Maintenant la vue sait quel titre afficher en fonction de la ligne à laquelle on se trouve ; vous pouvez lancer l'application et confirmer que tout fonctionne correctement !

Gif avec demonstration de scrolling sur notre PickerView
Scrolling sur notre PickerView

Voici le code complet de nos deux extensions :

extension FormViewController: UIPickerViewDataSource {
    func numberOfComponents(in pickerView: UIPickerView) -> Int {
       1
    }

    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
       return dogRaces.count
    }
}

extension FormViewController: UIPickerViewDelegate {
    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
       return dogRaces[row]
    }
}

En résumé

  • En MVC, une vue peut utiliser le mécanisme de la dataSource pour ne pas être propriétaire de ses données, et les réclamer au contrôleur.

  • En MVC, une vue peut utiliser le mécanisme du delegate pour déléguer les prises de décision au contrôleur.

  • Un protocole est une liste de méthodes vides et de propriétés.

  • Pour utiliser le mécanisme de dataSource ou delegate, on suit trois étapes :

    1. La vue nomme le contrôleur sa dataSource et/ou delegate.

    2. Le contrôleur s'engage à répondre aux questions de la vue en adoptant le protocole correspondant.

    3. Le contrôleur répond aux questions de la vue en se conformant au(x) protocole(s), et donc en implémentant les méthodes de ce(s) dernier(s).

Dans le prochain chapitre, nous allons revenir sur nos champs de texte et apprendre à gérer le clavier !

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