• 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 la vue

Et nous voilà dans la partie sur le contrôleur ! Cette partie est la plus excitante du cours car nous allons tout connecter pour faire enfin fonctionner notre application ! La logique est prête dans le modèle, la vue est toute belle. Il ne nous reste plus qu'à faire les branchements avec le contrôleur !

Pour rappel, le rôle du contrôleur est de faire le lien entre le modèle et la vue. Le contrôleur va récupérer les données du modèle et les afficher dans la vue.

Dans ce chapitre, nous allons commencer par connecter le contrôleur et la vue.

Le fichier ViewController.swift

Concrètement le contrôleur, c'est le fichier ViewController.swift qui a été créé automatiquement par Xcode au début du projet.

Dans ce fichier, nous avons une classe ViewController qui hérite de UIViewController. Cette classe contient déjà deux méthodes :

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}

La première méthode est appelée lorsque le contrôleur a fini d'être chargé donc dans notre cas au lancement de l'application. Nous allons en avoir besoin dans le prochain chapitre pour faire quelques initialisations.

La deuxième méthode est appelée lorsque le contrôleur doit retenir trop d'informations et qu'il n'a plus de place en mémoire pour les stocker. Dans ce cas, il faut libérer de la place. Nous ne rencontrerons pas ce problème donc vous pouvez supprimer la méthode.

Les outlets

Pour connecter le contrôleur et la vue, nous allons utiliser des... connexions ! Il existe deux types de connexion : les outlets et les actions. Nous allons commencer par les outlets.

Les outlets et le MVC

Pour rappel, les outlets sont des connexions entre une vue dans le storyboard et une propriété dans le contrôleur. Nous allons donc créer des propriétés dans notre contrôleur qui représentent nos vues.

Cela veut dire que dans notre modèle MVC, le contrôleur s'adresse à la vue via les outlets.

Créer un outlet

Comment on crée les outlets ?

Vous avez deux options :

  • soit vous faites comme pour QuestionView précédemment en écrivant la propriété et le décorateur @IBOutlet.

  • soit vous laissez Xcode faire cela pour vous en utilisant le control drag. Et c'est ce que nous allons faire.

Placez-vous en mode assistant avec le storyboard à gauche et le contrôleur à droite. Laissez la touche ctrl enfoncée et glissez depuis le bouton vers le code comme ceci :

Lorsque vous relâchez la souris, une popup vous demande plus d'information sur la connexion que vous souhaitez créer. Il y a plusieurs paramètres :

  • Connection : le type de connexion que vous souhaitez créer. Ici, on veut bien un Outlet.

  • Object : Vers où pointe la connexion, c'est bien le ViewController.

  • Name : Le nom de la propriété que l'on veut créer. Ici nous allons écrire : newGameButton.

  • Type : Le type de la propriété que l'on veut créer. C'est bien UIButton.

  • Storage : C'est une notion assez avancée de programmation et vous n'aurez jamais besoin de changer ce paramètre donc ignorons-le pour le moment.

On peut ensuite cliquer sur connect et le code suivant est généré :

@IBOutlet weak var newGameButton: UIButton!

On a bien le décorateur @IBOutlet (puis le mot-clé weak que l'on ignore pour le moment), puis la déclaration d'une propriété newGameButton de type UIButton.

Vous pouvez répéter cette opération pour :

  • l'indicateur d'activité

  • la vue question

  • le label score

Si vous choisissez les noms ci-dessous, vous devrez obtenir le code suivant :

@IBOutlet weak var newGameButton: UIButton!
@IBOutlet weak var activityIndicator: UIActivityIndicatorView!
@IBOutlet weak var scoreLabel: UILabel!
@IBOutlet weak var questionView: QuestionView!

Tous nos outlets sont créés ! Nous pouvons désormais manipuler nos vues dans le contrôleur.

Les outlets collections

Pour votre information, il existe un autre type d'outlet que nous n'utiliserons pas ensemble, mais qui est très simple : les outlets collections. Comme leur nom le suggère, ils fonctionnent comme les outlets, mais pour plusieurs vues à la fois.

Par exemple, si on crée une application calculatrice, on pourrait créer un outlet pour chaque bouton de 0 à 9 comme ceci :

@IBOutlet weak var zeroButton: UIButton!
@IBOutlet weak var oneButton: UIButton!
@IBOutlet weak var twoButton: UIButton!
@IBOutlet weak var threeButton: UIButton!
@IBOutlet weak var fourButton: UIButton!
@IBOutlet weak var fiveButton: UIButton!
@IBOutlet weak var sixButton: UIButton!
@IBOutlet weak var sevenButton: UIButton!
@IBOutlet weak var eightButton: UIButton!
@IBOutlet weak var nineButton: UIButton!

C'est un peu redondant et peu pratique à utiliser. On va donc utiliser une outlet collection :

@IBOutlet var numbersButton: [UIButton]!

La propriété créée est un tableau qui contient tous les boutons.

Pour créer une outlet collection, il suffit de choisir ce type de connexion dans la popup après le control drag. Puis pour remplir le tableau, il faut faire un control drag depuis chaque bouton vers la propriété numbersButton.

Les actions

Nous voulons non seulement contrôler les vues. Mais aussi recevoir des informations de leur part. Par exemple, comment faire pour exécuter du code lorsque le bouton est tapé ?

Les actions et le MVC

Nous devons donc aborder l'épineuse question de la communication de la vue vers le contrôleur. La vue a-t-elle le droit de communiquer avec le contrôleur ?

Et la réponse est oui... mais indirectement. Via les actions. Les actions sont des connexions entre une vue et une méthode du contrôleur associées à un évènement.

Schématiquement le contrôleur va placer une cible sur lui-même. Ainsi la vue va pouvoir lui envoyer un message à chaque fois que le bouton est tapé.

Créer une action

Pour créer une action, on va faire comme pour les outlets. On va effectuer un control drag depuis le bouton vers le code. Mais cette fois-ci, nous allons choisir Action pour le paramètre Connection :

Il y à nouveau plusieurs paramètres ici :

  • Connection et Object : même chose que pour les outlets.

  • Name : le nom de la méthode que nous allons créer. Ici je vous propose didTapNewGameButton.

  • Type : Nous allons créer une méthode. Cette méthode peut avoir des paramètres comme l'évènement et la vue dont vient l'action, appelée le sender. Si on souhaite avoir le sender en paramètre de la méthode, on peut choisir ici le type du sender.

  • Event : Une action est associée à un évènement. Cet évènement représente le geste que doit réaliser l'utilisateur pour que l'action ait lieu. Par défaut, pour un bouton, cet évènement est Touch Up Inside. Cela signifie un touché vers le haut à l'intérieur du bouton, c'est-à-dire le moment où le doigt quitte le bouton. Je vous invite à regarder la liste pour voir les autres types d'évènements possibles.

  • Arguments : Comme expliqué précédemment, la méthode peut avoir des paramètres. Ici on décide si on en a besoin ou non. J'ai choisi None pour aucun paramètre. Pour information, les autres possibilités sont le sender seul ou le sender et l'évènement.

Vous pouvez cliquer sur connect et Xcode génère le code suivant :

@IBAction func didTapNewGameButton() {
}

Notre action est créée. La méthode didTapNewGameButton va être appelée à chaque fois que l'on clique sur le bouton.

Pour différencier l'action et la logique qu'elle implique, je vous recommande de créer deux méthodes séparées. Nous allons donc rajouter une méthode startNewGame privée à notre classe :

private func startNewGame() {
}

Cette méthode va pouvoir être appelée n'importe où dans le code et notamment dans la méthode didTapNewGameButton :

@IBAction func didTapNewGameButton() {
    startNewGame()
}

Implémenter startNewGame

Il ne nous reste plus qu'à implémenter notre méthode startNewGame. Réfléchissons un peu à ce qu'elle fait. Elle lance une nouvelle partie donc :

  1. Elle va afficher une interface de chargement

  2. Elle va lancer le chargement des questions

  3. Lorsque les questions sont chargées, la partie peut débuter.

Nous allons nous concentrer sur les points 2 et 3 dans le prochain chapitre car nous avons besoin du modèle pour ça. Pour le moment, nous allons afficher notre interface de chargement. Voici à quoi elle va ressembler :

Nous allons donc :

  • Cacher le bouton : cela permet d'empêcher l'utilisateur de lancer un nouveau chargement.

  • Afficher l'indicateur d'activité : pour notifier l'utilisateur que le chargement est en cours.

  • Remettre le score à zéro.

  • Remettre la vue question dans le style standard : son style a pu avoir été modifié plus tôt si l'utilisateur était en train de jouer.

  • Afficher "Loading..." dans la vue question.

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

Comme toutes les vues, les classes UIButton et UIActivityIndicatorView héritent de UIView. Elles ont donc accès à la propriété isHidden que nous avons vue dans la partie précédente. Nous avons donc simplement à écrire :

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

Ainsi, le bouton est caché et l'indicateur d'activité est visible.

La vue question

Nous avons déjà bien travaillé sur notre vue question. Nous avons laissé deux propriétés publiques style et title. Nous avons juste à les utiliser ici.

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

Vous voyez en quoi avoir créé une vue customisée nous rend les choses faciles ici !

Le label score

Il ne nous reste plus qu'à remettre à jour le label score. Vous savez maintenant que pour changer le texte d'un UILabel, on utilise la propriété text. C'est donc précisément ce que nous allons faire :

scoreLabel.text = "0 / 10"

Et voilà ! Votre méthode startNewGame doit ressembler à ceci :

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

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

    scoreLabel.text = "0 / 10"
}

Nous avons maintenant une belle interface de chargement lorsqu'on clique sur le bouton. Vous pouvez tester cela en lançant le simulateur.

En résumé

  • Pour contrôler les vues, le contrôleur utilise les outlets qui sont des connexions entre une propriété du contrôleur et une vue.

  • Pour envoyer un message au contrôleur, les vues peuvent utiliser les actions qui sont des connexions entre une méthode du contrôleur et une vue, associées à un évènement.

  • Pour créer ces connexions, on utilise le control drag depuis la vue vers le code du contrôleur.

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