• 30 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 02/11/2023

Connectez le contrôleur et le modèle

Nous avons fait la moitié du travail. La vue et le contrôleur sont connectés, mais le modèle et le contrôleur ne communiquent pas encore. Ce ne sera plus vrai à la fin de ce chapitre !

La propriété game

Dans le chapitre précédent, on a vu que le contrôleur peut manipuler les vues grâce à des outlets. Cela prend la forme de propriétés. Ici, nous allons faire exactement la même chose. Nous allons créer une propriété game de type Game :

var game = Game()

Cette propriété va nous permettre de gérer la partie en nous appuyant sur le travail qui a été fait dans le modèle.

Donc si on reprend notre modèle MVC, on voit que pour accéder au modèle, le contrôleur utilise les propriétés :

Nous allons maintenant utiliser notre propriété game pour lancer une nouvelle partie en téléchargeant de nouvelles questions.

Charger les questions

Pour télécharger de nouvelles questions, nous allons utiliser la méthode refresh de la classe Game que nous avons créée ensemble. Et nous allons faire cela, lorsque l'utilisateur appuie sur le bouton pour lancer une nouvelle partie. Donc on va rajouter l'appel à la méthode refresh dans la méthode startNewGame.

private func startNewGame() {
    activityIndicator.isHidden = false
    newGameButton.isHidden = true

    questionView.title = "Loading..."
    questionView.style = .standard

    scoreLabel.text = "0 / 10"

    game.refresh()
}

Revenons un peu sur le fonctionnement de cette méthode :

  1. Elle redémarre la partie en remettant le score à zéro notamment

  2. Elle va chercher de nouvelles questions sur internet

  3. Quand les questions sont chargées :

    • Elle stocke les questions dans la propriété questions de la classe Game

    • Elle envoie une notification pour prévenir que les questions sont chargées.

Les notifications sont un des moyens qu'a le modèle de s'adresser au contrôleur. Nous avons déjà géré l'envoi de la notification, mais pas la réception.

C'est ce que nous allons faire ici !

Recevoir une notification

Pour recevoir une notification, il faut se brancher à l'émission de radio que l'on souhaite. Et nous voulons faire cela dès que le contrôleur est chargé pour ne pas rater de message.

Le nom de la notification

Lorsque le contrôleur vient de finir de se charger, la méthode viewDidLoad est appelée. C'est dans cette méthode que nous allons nous brancher à la notification.

Pour rappel, les notifications ont des noms. Ces noms permettent d'identifier chaque notification de façon unique. Pour savoir quelle notification écouter, nous allons commencer par obtenir son nom :

override func viewDidLoad() {
    super.viewDidLoad()
    let name = Notification.Name(rawValue: "QuestionsLoaded")
}

Jusque là rien de nouveau, nous avons écrit cette même ligne dans le modèle.

Observer la notification

Maintenant nous allons effectivement nous brancher. Pour rappel, la classe en charge d'envoyer et de recevoir les notifications s'appelle le NotificationCenter. Et le centre par défaut s'appelle simplement default. La méthode qui permet d'écouter les notifications s'appelle addObserver. Donc nous allons écrire :

let name = Notification.Name(rawValue: "QuestionsLoaded")
NotificationCenter.default.addObserver(self, selector: <vide>, name: name, object: nil)

Alors, commentons un peu cette ligne. Tout d'abord on récupère l'instance default de NotificationCenter sur laquelle on appelle la fonction addObserver. Cette fonction prend quatre paramètres :

  • observer : ici celui qui doit observer la notification, c'est le contrôleur. Donc on écrit self pour faire référence à soi-même.

  • selector : on va voir ça dans un instant.

  • name : le nom de la notification à observer, on lui passe donc la variable name que nous venons de créer.

  • object : cette propriété permet de préciser l'objet dont on accepte la notification. Cela fonctionne comme un filtre. Ici, on ne cherche pas à savoir d'où vient la notification, donc on écrit nil.

Le sélecteur

Revenons sur cet étrange paramètre selector. Il est du type Selector. Ce type permet de passer une fonction en paramètre. C'est cette fonction qui sera exécutée quand le contrôleur recevra la notification.

Commençons donc par créer une fonction :

func questionsLoaded() {
}

Ici, on est victime d'un petit héritage du langage Objective-C. Pour que la fonction soit accessible pour le sélecteur on doit rajouter  @objc  devant la déclaration :

@objc func questionsLoaded() {
}

Pour passer cette fonction dans le sélecteur, il y a une syntaxe bien particulière. On écrit #selector et le nom de la fonction entre parenthèses. Finalement, on obtient :

override func viewDidLoad() {
    super.viewDidLoad()
    let name = Notification.Name(rawValue: "QuestionsLoaded")
    NotificationCenter.default.addObserver(
        self, selector: #selector(questionsLoaded), 
        name: name, object: nil)
}

func questionsLoaded() {
}

Ça y est ! Notre contrôleur observe maintenant la notification et dès que les questions seront chargées, il sera prévenu et pourra exécuter la méthode questionsLoaded.

Implémenter questionsLoaded

Il ne nous reste plus qu'à implémenter la méthode questionsLoaded. Lorsque les questions sont chargées, nous allons faire plusieurs choses :

  1. Cacher l'indicateur d'activité : le chargement est terminé, il peut disparaître.

  2. Montrer le bouton : la partie a commencé, l'utilisateur peut décider d'en charger tout de suite une nouvelle s'il n'aime pas la première question qu'on lui propose.

  3. Afficher la première question de la partie.

Cacher l'indicateur d'activité et montrer le bouton

Vous commencez à être habitué maintenant à cacher et à montrer des choses grâce à la propriété isHidden. Donc je vous laisse essayer et je vous donne la correction :

// Essayez par vous même













func questionsLoaded() {
    activityIndicator.isHidden = true
    newGameButton.isHidden = false
}

Afficher la première question

Nous avons conçu notre classe Game pour qu'elle puisse facilement donner au contrôleur la question qu'il a besoin d'afficher. Nous avons fait cela en créant la propriété currentQuestion de type Question. Nous allons donc l'utiliser pour modifier le titre de notre vue question :

questionView.title = game.currentQuestion.title

Et voilà, nous n'avons plus qu'à tester dans le simulateur !

Il ne nous reste qu'une petite chose à faire. Lorsque l'utilisateur lance l'application, il s'attend à pouvoir jouer tout de suite. Donc nous allons lancer une partie dès la méthode viewDidLoad :

override func viewDidLoad() {
    super.viewDidLoad()
    let name = Notification.Name(rawValue: "QuestionsLoaded")
    NotificationCenter.default.addObserver(
        self, selector: #selector(questionsLoaded), 
        name: name, object: nil)
    startNewGame() // On lance une partie tout de suite
}

En résumé

  • Le contrôleur peut faire appel au modèle via ses propriétés.

  • Le modèle peut communiquer avec le contrôleur via des notifications. Pour observer une notification, on utilise la méthode addObserver de la classe NotificationCenter.

  • Les sélecteurs permettent de passer en paramètre une fonction avec la syntaxe suivante : #selector(nomDeLaFonction).

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