Si vous vous êtes amusé à jouer un peu avec votre formulaire, vous vous êtes sans doute aperçu que sans que vous ayez quoi que ce soit à faire, votre clavier apparaît automatiquement lorsque vous cliquez sur un champ de texte.
C'est pratique, mais du coup ça prend une bonne partie de l'écran, et on ne peut plus cliquer sur le bouton Valider...
Dans ce chapitre, nous allons apprendre à gérer la disparition du clavier !
Rappel sur le champ de texte
Tant qu'on en est à parler du clavier, laissez-moi vous faire un petit rappel sur ce qu'on a vu avec le champ de texte.
Le clavier est entièrement lié au champ de texte.
Par exemple, c'est le champ de texte qui décide du type de clavier qui s'affiche. On a ensemble modifié la propriété Keyboard Type
pour que le champ de texte Téléphone affiche plutôt le clavier suivant.
Cela veut dire que c'est également le champ de texte qui décide de l'apparition et de la disparition du clavier.
Clavier dans le simulateur
Si vous lancez l'application dans le simulateur, le clavier ne s'affiche peut-être pas. Cela est dû à un réglage spécial du simulateur. Quand vous interagissez avec une application dans le simulateur, vous avez l'option d'utiliser le clavier iOS présent sur l'écran du simulateur, ou d'utiliser le clavier de votre Mac.
Vous pouvez alterner entre les deux options en allant dans Hardware > Keyboard > Connect Hardware Keyboard. Vous pouvez également utiliser le raccourci shift + cmd + K .
Une tape et... disparition !
Le clavier apparaît donc automatiquement, mais il ne disparaît pas ! Le réflexe commun, c'est de cliquer n'importe où en dehors du champ de texte pour faire disparaître le clavier. Alors, essayons de rajouter cette fonctionnalité à notre page !
Ajout du Tap Gesture Recognizer
Pour y parvenir, il faut associer le geste "Tap" à "n'importe où sur la page". Et vous savez déjà faire cela ! Nous allons utiliser UITapGestureRecognizer
, et nous allons le relier à la vue principale du FormViewController
.
Sélectionnez Tap Gesture Recognizer
dans la bibliothèque des objets :
Et glissez-le sur la vue principale. Je vous suggère d'utiliser le panneau latéral gauche dans votre storyboard (Document Outline) pour être certain d'atterrir au bon endroit.
Le Tap Gesture Recognizer est maintenant ajouté et relié à la vue principale.
Création de l'action
Nous allons maintenant pouvoir créer l'action correspondant à notre Tap Gesture Recognizer
. Vous savez le faire, il suffit de faire un control-drag depuis le Tap Gesture Recognizer
vers le code. Vous pouvez nommer l'action dismissKeyboard
.
Le code suivant est généré :
@IBAction func dismissKeyboard(_ sender: UITapGestureRecognizer) {
}
Dans cette méthode, nous allons rédiger le code permettant de cacher le clavier.
À vos outlets
On l'a dit, c'est la responsabilité du champ de texte de cacher le clavier. Donc nous allons avoir besoin d'accéder aux champs de texte dans le code. Pour cela, créez deux outlets correspondant aux deux champs de texte.
Voici mon résultat :
@IBOutlet weak var nameTextField: UITextField!
@IBOutlet weak var phoneTextField: UITextField!
La notion de firstResponder
Tout est prêt ! Nous avons le geste, l'action et les outlets. Il ne reste plus qu'à rédiger la commande. Et cette commande, elle ressemble à ceci :
nameTextField.resignFirstResponder()
Pourquoi ça ne s'appelle pas nameTextField.hideKeyboard()
? C'est quoi un responder
?
Très bonnes questions ! En iOS, un responder
(répondeur, en français) est un objet qui peut répondre à des évènements et les gérer. En gros, toutes les vues sont des répondeurs, car elles peuvent toutes au moins gérer des gestes sur l'écran tactile.
Le premier répondeur ( firstResponder
) est l'objet qui est en train d'être utilisé. Il ne peut y en avoir qu'un seul. Par exemple, lorsqu'on tape du texte dans un champ de texte, il n'y a qu'un seul champ de texte qui répond. On ne peut pas taper du texte sur plusieurs champs en même temps.
Autre exemple, mettons que j'aie deux boutons l'un en dessous de l'autre, et que j'appuie dessus. Techniquement, les deux boutons ont été touchés. Mais un seul lancera son action ; on l'appelle le firstResponder .
Lorsque l'on touche le champ de texte, il devient firstResponder
. Et lorsqu'un champ de texte devient firstResponder , il affiche le clavier. N'importe quelle vue peut devenir le firstResponder
en appelant la méthode :
nameTextField.becomeFirstResponder()
À l'inverse, n'importe quelle vue peut décider de ne plus être le firstResponder
et pour cela, elle utilise la méthode qu'on a vue au-dessus :
nameTextField.resignFirstResponder()
Lorsqu'un champ de texte n'est plus firstResponder
, cela veut dire que l'utilisateur n'est plus en train d'interagir avec lui, et que du coup le clavier n'a plus de raison d'être présenté. Il disparaît.
Vous pouvez faire la même chose avec phoneTextField
, et votre action devient :
@IBAction func dismissKeyboard(_ sender: UITapGestureRecognizer) {
nameTextField.resignFirstResponder()
phoneTextField.resignFirstResponder()
}
Vous pouvez tester dans le simulateur, et ça marche !
Et la touche de retour ?
La touche de retour sur le clavier, c'est celle-ci :
Lorsqu'on appuie sur cette touche, l'utilisateur peut vraisemblablement s'attendre à ce que le clavier disparaisse. Et pour cela, il faut que la vue puisse prévenir le contrôleur de l'évènement "a appuyé sur la touche retour". Si seulement elle pouvait lui déléguer la....
On va utiliser un delegate !
Mais quelle vivacité d'esprit ! Je suis ébahi. Je vous adore !
En effet, on va utiliser un delegate
. Et on suit toujours les mêmes étapes :
Le champ de texte va nommer le contrôleur son delegate.
Le contrôleur s'engage à répondre aux questions du champ de texte en adoptant le protocole correspondant.
Le contrôleur répond aux questions en se conformant au protocole, et donc en implémentant les méthodes de ce dernier.
1. Le champ de texte nomme le contrôleur comme son delegate
Pour cela, vous vous souvenez, on utilise un control-drag depuis le champ de texte vers le contrôleur, et dans la pop-up on clique sur delegate.
Vous pouvez répéter l'opération pour le deuxième champ de texte.
2. Le contrôleur adopte le protocole
Le protocole correspondant s'appelle UITextFieldDelegate
. Oui... ils ne font pas dans la créativité. En même temps, heureusement !
Et pour que le contrôleur l'adopte, il faut ajouter une extension à la classe, comme nous l’avons vu dans le chapitre précédent.
extension FormViewController: UITextFieldDelegate {
}
3. Le contrôleur se conforme au protocole
Il ne nous reste maintenant plus qu'à implémenter la méthode qui nous intéresse dans ce protocole, et qui correspond à l'événement "a appuyé sur la touche retour". Cet évènement s'appelle :
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
}
Cette fonction prend en paramètre un textField
. C'est le champ de texte qui vient de subir l'évènement "a appuyé sur la touche retour". Et elle renvoie un booléen qui doit être à true si vous souhaitez que la méthode suive son implémentation normale, ce qui sera presque toujours le cas.
Mais avant de renvoyer le booléen, on peut faire ce qu'on veut, et notamment :
textField.resignFirstResponder()
Et maintenant le champ de texte disparaîtra lorsqu'on appuiera sur la touche de retour !
Voici le code complet de la fonction :
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
Allez plus loin
Un dernier cas courant que vous rencontrerez en manipulant les claviers, c'est celui d'un clavier qui recouvre un champ de texte. C'est notamment le cas si vos champs de texte sont situés en bas de l'écran.
Dans ce cas, l'utilisateur ne voit pas ce qu'il écrit !
Il y a deux solutions :
Ne pas mettre de champ de texte en bas. C'est évident, mais je le précise parce que ça vaut parfois le coup de modifier le design de son application dans ce but.
Faire glisser la vue principale vers le haut pour que le champ de texte soit plus haut que le clavier. Dans ce cas, c'est bien toute la vue principale qui glisse vers le haut, pour ne pas altérer le design.
Pour réaliser la deuxième solution, il va falloir effectuer les actions suivantes :
Écouter les notifications d’affichage et de disparition du clavier.
Déplacer toute la vue vers le haut ou vers le bas en fonction de la notification reçue.
Pour cela, rien de plus simple. iOS fournit deux notifications pour déterminer l’affichage et la disparition du clavier, que nous pouvons écouter via le NotificationCenter
.
Commencer par créer deux méthodes pour capter les notifications :
@objc func keyboardAppear(_ notification: Notification) {
}
@objc func keyboardDisappear(_ notification: Notification) {
}
Puis demander au NotificationCenter d’utiliser ces deux fonctions en cas d’affichage ou de disparition du clavier :
NotificationCenter.default.addObserver(self, selector: #selector(keyboardAppear(_:)), name: UIViewController.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardDisappear(_:)), name: UIViewController.keyboardWillHideNotification, object: nil)
Ensuite, dans la méthode keyboardAppear
, nous allons faire en sorte de remonter notre vue pour que le bas de la vue soit toujours au-dessus du clavier :
@objc func keyboardAppear(_ notification: Notification) {
guard let frame = notification.userInfo?[UIViewController.keyboardFrameEndUserInfoKey] as? NSValue else { return }
let keyboardFrame = frame.cgRectValue
if self.view.frame.origin.y == 0 {
self.view.frame.origin.y -= keyboardFrame.height
}
}
Puis faire l’inverse dans le cas de keyboardDisappear
:
@objc func keyboardDisappear(_ notification: Notification) {
if self.view.frame.origin.y != 0 {
self.view.frame.origin.y = 0
}
}
Et voici le résultat :
Toutefois, comme vous pouvez le remarquer, tout ce qui est en haut de l’écran disparaît. Cette solution n’est pas toujours idéale.
Rassurez-vous, il en existe d'autres, comme l’utilisation de Scroll View, ou bien encore la possibilité de ne pas remonter complètement l’écran mais uniquement une partie. Toutefois je ne détaillerai pas toutes les solutions dans ce cours. Je vous invite à consulter les réponses sur ce Stack Overflow, il vous donnera quelques pistes de solutions. N'hésitez pas à faire vos propres recherches sur Internet, car les solutions évoluent aussi avec les mises à jour d’iOS.
En résumé
Le champ de texte est l'objet qui gère le clavier.
On peut utiliser un
Tap Gesture Recognizer
associé à la vue principale, pour faire disparaître le clavier après l'appui sur n'importe quel écran.Lorsque le champ de texte devient
firstResponder
, le clavier apparaît. Lorsque le champ de texte ne l'est plus, le clavier disparaît. Deux méthodes sont associées à ces évènements :textField.becomeFirstResponder()
;textField.resignFirstResponder()
.
Pour que le clavier disparaisse à l'appui sur la touche de retour, on utilise le
UITextFieldDelegate
, et en particulier la méthodetextFieldShouldReturn
.
Dans le prochain chapitre, nous allons apprendre à récupérer les données de notre formulaire !