{"id":2822,"date":"2016-08-18T11:59:38","date_gmt":"2016-08-18T15:59:38","guid":{"rendered":"\/cours\/xcode\/wp\/?page_id=2822"},"modified":"2016-08-18T11:59:38","modified_gmt":"2016-08-18T15:59:38","slug":"caissier-electronique","status":"publish","type":"page","link":"https:\/\/ve2cuy.com\/xcode\/caissier-electronique\/","title":{"rendered":"Caissier.\u00c9lectronique"},"content":{"rendered":"<h2>Atelier anim\u00e9 par l&rsquo;enseignant<\/h2>\n<h2><span style=\"color: #ff0000;\">Note: \u00a0R\u00e9alis\u00e9 sous Xcode 9B6<\/span><\/h2>\n<h1>\u00c9tape 1 &#8211; R\u00e9alisation de la maquette<\/h1>\n<hr \/>\n<p><span style=\"color: #008080;\"><strong>Objectif<\/strong><\/span>: \u00a0R\u00e9aliser une mise en page qui fonctionne en mode portrait et en mode paysage.<br \/>\nVoici la maquette finale:<\/p>\n<h1><\/h1>\n<h1><a href=\"\/xcode\/wp-content\/uploads\/2016\/08\/caissier-electronique-02.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-2825\" src=\"\/xcode\/wp-content\/uploads\/2016\/08\/caissier-electronique-02.png\" alt=\"caissier-electronique-02\" width=\"1148\" height=\"709\" \/><\/a><\/h1>\n<p>Ressources<br \/>\n<a href=\"\/xcode\/wp-content\/uploads\/2016\/08\/calculer.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-2828\" src=\"\/xcode\/wp-content\/uploads\/2016\/08\/calculer.png\" alt=\"calculer\" width=\"256\" height=\"256\" \/><\/a> <a href=\"\/xcode\/wp-content\/uploads\/2016\/08\/123.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-2829\" src=\"\/xcode\/wp-content\/uploads\/2016\/08\/123.png\" alt=\"123\" width=\"418\" height=\"287\" \/><\/a><br \/>\n<span style=\"color: #ff0000;\"><strong>Action<\/strong><\/span>: \u00a0R\u00e9aliser la maquette<br \/>\n<strong><span style=\"color: #008080;\">Note<\/span><\/strong>: \u00a0Les champs de saisie sont de type\u00a0<em>UITextField<\/em> sauf pour tps et tv qui sont de type <em>UILabel<\/em>.<br \/>\n<span style=\"color: #ff0000;\"><strong>Action<\/strong><\/span>: \u00a0Tester l&rsquo;application dans le simulateur (orientation portrait et paysage). \u00a0Faire les ajustements au besoin.<br \/>\n&nbsp;<\/p>\n<h1>\u00c9tape 2 &#8211; D\u00e9finir les liens MVC &#8211; @IBOUTLET et @IBAction<\/h1>\n<hr \/>\n<p><span style=\"color: #ff0000;\"><strong>Action<\/strong><\/span>: \u00a0D\u00e9finir les liens suivants:<\/p>\n<pre class=\"lang:swift decode:true\">    @IBOutlet weak var quantit\u00e9: UITextField!\n    @IBOutlet weak var prix: UITextField!\n    @IBOutlet weak var total: UITextField!\n    @IBOutlet weak var totalAvecTaxes: UITextField!\n    @IBOutlet weak var tps: UILabel!\n    @IBOutlet weak var tvq: UILabel!\n    @IBAction func calculerTotalAvecTaxes(_ sender: AnyObject) {\n    } \/\/ func calculerTotalAvecTaxes\n<\/pre>\n<p><strong><span style=\"color: #ff0000;\">Action<\/span><\/strong>: Au d\u00e9marrage, initialiser les champs num\u00e9riques \u00e0 &lsquo;<em>nil<\/em>&lsquo;:<\/p>\n<pre class=\"lang:swift decode:true \">    override func viewDidLoad() {\n        super.viewDidLoad()\n        quantit\u00e9.text = nil\n        prix.text = nil\n        total.text = nil\n        tps.text = nil\n        tvq.text = nil\n        totalAvecTaxes.text = nil\n    }  \/\/ viewDidLoad()<\/pre>\n<p>&nbsp;<\/p>\n<h1>\u00c9tape 3 &#8211; Programmer la d\u00e9l\u00e9gation<\/h1>\n<hr \/>\n<h1><\/h1>\n<p><span style=\"color: #ff0000;\"><strong>Action: <\/strong><span style=\"color: #000000;\">Adh\u00e9rer au protocole UITextFieldDelegate.<\/span><\/span><\/p>\n<pre class=\"lang:swift decode:true\">class ViewController: UIViewController, UITextFieldDelegate {\n<\/pre>\n<p><span style=\"color: #ff0000;\"><strong>Action:<\/strong><\/span> \u00c9tablir un lien entre un composant et un protocole.<br \/>\n<a href=\"\/xcode\/wp-content\/uploads\/2016\/08\/delegate.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-2835\" src=\"\/xcode\/wp-content\/uploads\/2016\/08\/delegate.png\" alt=\"delegate\" width=\"260\" height=\"104\" \/><\/a><br \/>\n<span style=\"color: #ff0000;\"><strong>Action<\/strong><\/span>: Exp\u00e9rimenter avec les m\u00e9thodes du protocole\u00a0<strong>UITextFieldDelegate<\/strong>:<br \/>\n<a href=\"\/xcode\/wp-content\/uploads\/2016\/08\/textFieldDelegate.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-2836\" src=\"\/xcode\/wp-content\/uploads\/2016\/08\/textFieldDelegate.png\" alt=\"textFieldDelegate\" width=\"1025\" height=\"231\" \/><\/a><\/p>\n<pre class=\"lang:swift decode:true\">    func textFieldDidBeginEditing(_ textField: UITextField) {\n        print(\"#L'utilisateur est entr\u00e9 dans la zone de texte!\")\n        textField.textColor = UIColor.red\n    }\n    func textFieldDidEndEditing(_ textField: UITextField) {\n        textField.textColor = UIColor.black\n    }\n    func textFieldShouldReturn(_ textField: UITextField) -&gt; Bool {\n        print(\"#L'utilisateur a appuy\u00e9 sur la touche retour!\")\n        return true\n    }\n<\/pre>\n<p>&nbsp;<\/p>\n<h1>\u00c9tape 4 &#8211; Programmer les fonctionnalit\u00e9s de l&rsquo;application<\/h1>\n<hr \/>\n<p><a href=\"https:\/\/bitbucket.org\/alain_boudreault\/exercice-sur-textfielddelegate\">R\u00e9vision prof<\/a><\/p>\n<h3>Tester si un champ est vide<\/h3>\n<p>Il est possible de tester si un champ est vide de la fa\u00e7on suivante:<\/p>\n<pre class=\"lang:swift mark:3 decode:true \">    func textFieldDidEndEditing(_ textField: UITextField) {\n        textField.textColor = UIColor.black\n        if textField.text!.isEmpty == true {\n          afficherAlert(\"Le champ de saisie ne doit pas \u00eatre vide!\")\n        }\n    }\n<\/pre>\n<h3><\/h3>\n<h3>Afficher une fen\u00eatre d&rsquo;alerte<\/h3>\n<p>Voici comment afficher une fen\u00eatre d&rsquo;alerte:<\/p>\n<pre class=\"lang:swift decode:true\">    func afficherAlert(_ message:String) {\n        let alert = UIAlertController(title: \"Attention!\", message: message, preferredStyle: UIAlertControllerStyle.alert)\n        alert.addAction(UIAlertAction(title: \"Je ferai plus attention!\", style: UIAlertActionStyle.default, handler: nil))\n        \/\/ Patch temporaire pour Xcode 8b5; utilisation de 'DispatchQueue' pour ex\u00e9cuter le self.present()\n        DispatchQueue.main.async( execute: { self.present(alert,  animated: true, completion: nil) } )\n        \/\/self.present(alert,  animated: true, completion: nil)\n    }\n<\/pre>\n<p>&nbsp;<br \/>\nDans notre cas, il suffit de tester s&rsquo;il est possible de le convertir en nombre:<\/p>\n<pre class=\"lang:swift decode:true\">if let _ = Float(textField.text!) {\n  \/\/ calculer le total ...\n} else\n{\n  afficherAlert(\"Erreur, Il faut entrer un nombre!\")\n}\n<\/pre>\n<p>&nbsp;<\/p>\n<h3><span style=\"color: #ff0000;\"><span style=\"color: #000000;\">Quelques conversions num\u00e9riques<\/span><\/span><\/h3>\n<p>Le contenu d&rsquo;un <em>UITextField<\/em> et d&rsquo;un <em>UILabel<\/em> est accessible via la propri\u00e9t\u00e9 &lsquo;text&rsquo;, comme dans &lsquo;quantit\u00e9.text&rsquo;.<br \/>\nLa propri\u00e9t\u00e9 &lsquo;text&rsquo; est de type &lsquo;String&rsquo;.<br \/>\nAvant de pouvoir utiliser son contenu \u00e0 des fins arithm\u00e9tiques il faudra le convertir.<br \/>\nLe fa\u00e7on la plus simple, est d&rsquo;utiliser le constructeur des types num\u00e9riques.<br \/>\nPar exemple,<\/p>\n<ul>\n<li>Int(\u00ab\u00a0123\u00a0\u00bb)<\/li>\n<li>Float(\u00ab\u00a03.1415\u00a0\u00bb)<\/li>\n<li>Double(\u00ab\u00a01.23456789\u00a0\u00bb)<\/li>\n<\/ul>\n<p><span style=\"color: #ff0000;\"><span style=\"color: #000000;\">Si la chaine repr\u00e9sente un nombre valide pour le type, elle\u00a0sera alors convertie. \u00a0Si non, la m\u00e9thode retournera &lsquo;nil&rsquo;. \u00a0<\/span><\/span><br \/>\n<span style=\"color: #ff0000;\"><span style=\"color: #000000;\">Cette m\u00e9thode retourne une valeur \u00a0&lsquo;<em>optionnelle<\/em>&lsquo;.<\/span><\/span><br \/>\nPour obtenir le r\u00e9sultat, il faudra le &lsquo;d\u00e9baller&rsquo;.<\/p>\n<h3>Voici comment faire de fa\u00e7on forc\u00e9e\u00a0(Forced Unwrapping):<\/h3>\n<blockquote><p>Int(\u00ab\u00a0123\u00a0\u00bb)!<\/p><\/blockquote>\n<p><span style=\"color: #ff0000;\"><span style=\"color: #000000;\">ou bien <\/span><\/span><\/p>\n<blockquote><p>Float(total.text!)!<\/p><\/blockquote>\n<p><span style=\"color: #ff0000;\"><span style=\"color: #000000;\"><span style=\"color: #ff0000;\"><strong>Note<\/strong><\/span>: \u00a0&lsquo;.text&rsquo; est aussi une valeur &lsquo;String&rsquo; <em>optionnelle<\/em>.<\/span><\/span><br \/>\nDonc, pour calculer la TPS sur le total nous pourrions faire ceci:<\/p>\n<pre class=\"lang:swift decode:true\">let TPS:Float = 0.05\nlet _tps = Float(total.text!)! * TPS<\/pre>\n<blockquote><p><span style=\"color: #ff0000;\"><strong>Note<\/strong><\/span>: Si nous tentons d\u2019utiliser le point d\u2019exclamation sur une variable optionnelle qui n\u2019a pas de valeur (nil), nous obtiendrons une erreur d\u2019ex\u00e9cution avec le message d\u2019erreur suivant : Fatal error: <strong>Unexpectedly found nil value while unwrapping an Optional value.<\/strong><br \/>\nDans l&rsquo;exemple suivant, nous utiliserons le d\u00e9ballage conditionnel.<\/p><\/blockquote>\n<h3>D\u00e9ballage conditionnel\u00a0(Optional Bindings)<\/h3>\n<p>Pour calculer le montant en fonction de la quantit\u00e9 et du prix:<\/p>\n<pre class=\"lang:swift decode:true\">if let _quantit\u00e9 = Float(quantit\u00e9.text!), let _prix = Float(prix.text!) {\n   total.text = String(_quantit\u00e9 * _prix)\n   \/\/ total.text = NSString(format: \"%2.2f\", _quantit\u00e9 * _prix) as String\n}\n<\/pre>\n<p><span style=\"color: #ff0000;\"><strong>Note<\/strong><\/span>: \u00a0La classe NSString offre plus de possibilit\u00e9s de manipulation de la chaine.<\/p>\n<blockquote><p>L&rsquo;expression <em><strong>&lsquo;NSString(format: \u00ab\u00a0%2.2f\u00a0\u00bb, _quantit\u00e9 * _prix)&rsquo;<\/strong>\u00a0<\/em>\u00a0permet de fixer \u00e0 2,\u00a0le nombre de chiffres apr\u00e8s le point (virgule).<br \/>\n<a href=\"https:\/\/developer.apple.com\/library\/mac\/documentation\/Cocoa\/Conceptual\/Strings\/Articles\/formatSpecifiers.html\">R\u00e9f\u00e9rence<\/a> pour les instructions de formatage (String Format Specifiers).<\/p><\/blockquote>\n<p><span style=\"color: #ff0000;\"><strong>Autre chose,<\/strong><\/span><\/p>\n<blockquote><p>Le calcul sera effectu\u00e9 que si\u00a0<em>Float(quantit\u00e9.text!) et Float(prix.text!)\u00a0<\/em>ne sont pas vides.<br \/>\n<em>&lsquo;if let&rsquo;<\/em> est un d\u00e9ballage conditionnel.<br \/>\nLe bloc &lsquo;vrai&rsquo; est ex\u00e9cut\u00e9 que si l&rsquo;affectation a r\u00e9ussi.<br \/>\nVoici la forme g\u00e9n\u00e9rale:<br \/>\nif let var = expression {<br \/>\nvrai<br \/>\n} else {<br \/>\nfaux<br \/>\n}<\/p><\/blockquote>\n<h3>Derniers d\u00e9tails<\/h3>\n<p>Voici le code final pour nos calculs<br \/>\nCalcul du total, suite \u00e0 un \u00ab\u00a0enter\u00a0\u00bb dans un champ:<\/p>\n<pre class=\"lang:swift decode:true \">    func textFieldShouldReturn(_ textField: UITextField) -&gt; Bool {\n        if let _ = Float(textField.text!) {\n            textField.resignFirstResponder()\n            if let _quantit\u00e9 = Float(quantit\u00e9.text!), let _prix = Float(prix.text!) {\n                \/\/ total.text = String(_quantit\u00e9 * _prix)\n                total.text = NSString(format: \"%2.2f\", _quantit\u00e9 * _prix) as String\n            } \/\/ if\n        } else\n        {\n             afficherAlert(\"Erreur, Il faut entrer un nombre!\")\n             return false\n        }  \/\/ if\n        return true\n    }  \/\/ textFieldShouldReturn<\/pre>\n<p>&nbsp;<br \/>\nCalcul du grand total (total + taxes):<\/p>\n<pre class=\"lang:swift decode:true \">    @IBAction func calculerTotalAvecTaxes(_ sender: AnyObject) {\n        if total.text?.isEmpty == false {\n            let _tps = Float(total.text!)! * TPS\n            let _tvq = Float(total.text!)! * TVQ\n            let _totalAvecTaxes = Float(total.text!)! + _tps + _tvq\n            tps.text = NSString(format: \"%2.2f$\", _tps) as String\n            tvq.text = NSString(format: \"%2.2f$\", _tvq) as String\n            totalAvecTaxes.text = NSString(format: \"%2.2f$\", _totalAvecTaxes) as String\n        } \/\/ if\n    } \/\/ func calculerTotalAvecTaxes\n<\/pre>\n<p>&nbsp;<br \/>\nEt finalement, calculer le grand total automatiquement si le total change:<br \/>\nAjouter le code suivant:<\/p>\n<pre class=\"lang:swift decode:true \">func textFieldShouldReturn(_ textField: UITextField) -&gt; Bool {\n    if let _ = Float(textField.text!) {\n        textField.resignFirstResponder()\n        if let _quantit\u00e9 = Float(quantit\u00e9.text!), let _prix = Float(prix.text!) {\n            \/\/ total.text = String(_quantit\u00e9 * _prix)\n            total.text = NSString(format: \"%2.2f\", _quantit\u00e9 * _prix) as String\n            calculerTotalAvecTaxes(self)\n        } \/\/ if\n    } else\n    {\n         afficherAlert(\"Erreur, Il faut entrer un nombre!\")\n        return false\n    }  \/\/ if\n    return true\n}  \/\/ textFieldShouldReturn<\/pre>\n<p>Note: \u00a0Xcode propose des classes de mise en forme de nombres, de dates, &#8230;<br \/>\nVoici un exemple de code qui affiche un &lsquo;Float&rsquo; en format mon\u00e9taire:<\/p>\n<pre class=\"lang:swift decode:true \">func formatCurrency(_ value: Float) -&gt; String {\n     let formatter = NumberFormatter()\n     formatter.numberStyle = .currency\n     formatter.maximumFractionDigits = 2\n     formatter.locale = Locale(identifier: Locale.current.identifier)\n     let result = formatter.string(from: value as NSNumber)\n     return result!\n}<\/pre>\n<p>&nbsp;<br \/>\nLe projet termin\u00e9 est disponible <a href=\"https:\/\/bitbucket.org\/alain_boudreault\/caissier-lectronique\/get\/fb25e75a843c.zip\">ici<\/a>.<\/p>\n<hr \/>\n<h6 style=\"text-align: right;\">Document r\u00e9dig\u00e9 par Alain Boudreault le 2016.08.18<\/h6>\n","protected":false},"excerpt":{"rendered":"<p>Atelier anim\u00e9 par l&rsquo;enseignant Note: \u00a0R\u00e9alis\u00e9 sous Xcode 9B6 \u00c9tape 1 &#8211; R\u00e9alisation de la maquette Objectif: \u00a0R\u00e9aliser une mise en page qui fonctionne en mode portrait et en mode paysage. Voici la maquette finale: Ressources Action: \u00a0R\u00e9aliser la maquette Note: \u00a0Les champs de saisie sont de type\u00a0UITextField sauf pour tps et tv qui sont [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-2822","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/ve2cuy.com\/xcode\/wp-json\/wp\/v2\/pages\/2822","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ve2cuy.com\/xcode\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/ve2cuy.com\/xcode\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/ve2cuy.com\/xcode\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/ve2cuy.com\/xcode\/wp-json\/wp\/v2\/comments?post=2822"}],"version-history":[{"count":0,"href":"https:\/\/ve2cuy.com\/xcode\/wp-json\/wp\/v2\/pages\/2822\/revisions"}],"wp:attachment":[{"href":"https:\/\/ve2cuy.com\/xcode\/wp-json\/wp\/v2\/media?parent=2822"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}