Objectif: Réaliser une mise en page qui fonctionne en mode portrait et en mode paysage.
Voici la maquette finale:
Ressources
Action: Réaliser la maquette
Note: Les champs de saisie sont de type UITextField sauf pour tps et tv qui sont de type UILabel.
Action: Tester l’application dans le simulateur (orientation portrait et paysage). Faire les ajustements au besoin.
Action: Définir les liens suivants:
@IBOutlet weak var quantité: UITextField! @IBOutlet weak var prix: UITextField! @IBOutlet weak var total: UITextField! @IBOutlet weak var totalAvecTaxes: UITextField! @IBOutlet weak var tps: UILabel! @IBOutlet weak var tvq: UILabel! @IBAction func calculerTotalAvecTaxes(_ sender: AnyObject) { } // func calculerTotalAvecTaxes
Action: Au démarrage, initialiser les champs numériques à ‘nil‘:
override func viewDidLoad() { super.viewDidLoad() quantité.text = nil prix.text = nil total.text = nil tps.text = nil tvq.text = nil totalAvecTaxes.text = nil } // viewDidLoad()
Action: Adhérer au protocole UITextFieldDelegate.
class ViewController: UIViewController, UITextFieldDelegate {
Action: Établir un lien entre un composant et un protocole.
Action: Expérimenter avec les méthodes du protocole UITextFieldDelegate:
func textFieldDidBeginEditing(_ textField: UITextField) { print("#L'utilisateur est entré dans la zone de texte!") textField.textColor = UIColor.red } func textFieldDidEndEditing(_ textField: UITextField) { textField.textColor = UIColor.black } func textFieldShouldReturn(_ textField: UITextField) -> Bool { print("#L'utilisateur a appuyé sur la touche retour!") return true }
Il est possible de tester si un champ est vide de la façon suivante:
func textFieldDidEndEditing(_ textField: UITextField) { textField.textColor = UIColor.black if textField.text!.isEmpty == true { afficherAlert("Le champ de saisie ne doit pas être vide!") } }
Voici comment afficher une fenêtre d’alerte:
func afficherAlert(_ message:String) { let alert = UIAlertController(title: "Attention!", message: message, preferredStyle: UIAlertControllerStyle.alert) alert.addAction(UIAlertAction(title: "Je ferai plus attention!", style: UIAlertActionStyle.default, handler: nil)) // Patch temporaire pour Xcode 8b5; utilisation de 'DispatchQueue' pour exécuter le self.present() DispatchQueue.main.async( execute: { self.present(alert, animated: true, completion: nil) } ) //self.present(alert, animated: true, completion: nil) }
Dans notre cas, il suffit de tester s’il est possible de le convertir en nombre:
if let _ = Float(textField.text!) { // calculer le total ... } else { afficherAlert("Erreur, Il faut entrer un nombre!") }
Le contenu d’un UITextField et d’un UILabel est accessible via la propriété ‘text’, comme dans ‘quantité.text’.
La propriété ‘text’ est de type ‘String’.
Avant de pouvoir utiliser son contenu à des fins arithmétiques il faudra le convertir.
Le façon la plus simple, est d’utiliser le constructeur des types numériques.
Par exemple,
Si la chaine représente un nombre valide pour le type, elle sera alors convertie. Si non, la méthode retournera ‘nil’.
Cette méthode retourne une valeur ‘optionnelle‘.
Pour obtenir le résultat, il faudra le ‘déballer’.
Int(« 123 »)!
ou bien
Float(total.text!)!
Note: ‘.text’ est aussi une valeur ‘String’ optionnelle.
Donc, pour calculer la TPS sur le total nous pourrions faire ceci:
let TPS:Float = 0.05 let _tps = Float(total.text!)! * TPS
Note: Si nous tentons d’utiliser le point d’exclamation sur une variable optionnelle qui n’a pas de valeur (nil), nous obtiendrons une erreur d’exécution avec le message d’erreur suivant : Fatal error: Unexpectedly found nil value while unwrapping an Optional value.
Dans l’exemple suivant, nous utiliserons le déballage conditionnel.
Pour calculer le montant en fonction de la quantité et du prix:
if let _quantité = Float(quantité.text!), let _prix = Float(prix.text!) { total.text = String(_quantité * _prix) // total.text = NSString(format: "%2.2f", _quantité * _prix) as String }
Note: La classe NSString offre plus de possibilités de manipulation de la chaine.
L’expression ‘NSString(format: « %2.2f », _quantité * _prix)’ permet de fixer à 2, le nombre de chiffres après le point (virgule).
Référence pour les instructions de formatage (String Format Specifiers).
Autre chose,
Le calcul sera effectué que si Float(quantité.text!) et Float(prix.text!) ne sont pas vides.
‘if let’ est un déballage conditionnel.
Le bloc ‘vrai’ est exécuté que si l’affectation a réussi.
Voici la forme générale:
if let var = expression {
vrai
} else {
faux
}
Voici le code final pour nos calculs
Calcul du total, suite à un « enter » dans un champ:
func textFieldShouldReturn(_ textField: UITextField) -> Bool { if let _ = Float(textField.text!) { textField.resignFirstResponder() if let _quantité = Float(quantité.text!), let _prix = Float(prix.text!) { // total.text = String(_quantité * _prix) total.text = NSString(format: "%2.2f", _quantité * _prix) as String } // if } else { afficherAlert("Erreur, Il faut entrer un nombre!") return false } // if return true } // textFieldShouldReturn
Calcul du grand total (total + taxes):
@IBAction func calculerTotalAvecTaxes(_ sender: AnyObject) { if total.text?.isEmpty == false { let _tps = Float(total.text!)! * TPS let _tvq = Float(total.text!)! * TVQ let _totalAvecTaxes = Float(total.text!)! + _tps + _tvq tps.text = NSString(format: "%2.2f$", _tps) as String tvq.text = NSString(format: "%2.2f$", _tvq) as String totalAvecTaxes.text = NSString(format: "%2.2f$", _totalAvecTaxes) as String } // if } // func calculerTotalAvecTaxes
Et finalement, calculer le grand total automatiquement si le total change:
Ajouter le code suivant:
func textFieldShouldReturn(_ textField: UITextField) -> Bool { if let _ = Float(textField.text!) { textField.resignFirstResponder() if let _quantité = Float(quantité.text!), let _prix = Float(prix.text!) { // total.text = String(_quantité * _prix) total.text = NSString(format: "%2.2f", _quantité * _prix) as String calculerTotalAvecTaxes(self) } // if } else { afficherAlert("Erreur, Il faut entrer un nombre!") return false } // if return true } // textFieldShouldReturn
Note: Xcode propose des classes de mise en forme de nombres, de dates, …
Voici un exemple de code qui affiche un ‘Float’ en format monétaire:
func formatCurrency(_ value: Float) -> String { let formatter = NumberFormatter() formatter.numberStyle = .currency formatter.maximumFractionDigits = 2 formatter.locale = Locale(identifier: Locale.current.identifier) let result = formatter.string(from: value as NSNumber) return result! }
Le projet terminé est disponible ici.