NEW: Learn SwiftUI with my free YouTube video series! >>

< Previous: Concevoir la mise en page de votre interface utilisateur   Next: Devinez quel drapeau : nombres aléatoires >

Faire fonctionner le jeu de base : UIButton et CALayer

Nous allons créer un tableau de chaînes de caractères qui contiendra tous les pays qui seront utilisés pour notre jeu, et en même temps, nous allons créer une autre propriété qui conservera le score actuel du joueur - c'est un jeu, après tout !

Commençons avec les nouvelles propriétés. Tapez ces deux lignes de code directement sous les lignes @IBOutlet que vous avez ajoutées précédemment dans ViewController.swift :

var countries = [String]()
var score = 0

Vous avez déjà rencontré la première ligne dans le projet 1 : elle crée une nouvelle propriété appelée countries qui contient un nouveau tableau de chaînes de caractères vide. Le second crée une nouvelle propriété appelée score qui est définie sur 0.

Ce que vous voyez ici s'appelle type inference (inférence de type). Cela signifie que Swift détermine de quel type de données est une variable ou une constante en se basant sur ce que vous y mettez. Cela signifie que a) vous devez mettre la bonne chose dans vos variables, sinon elles auront un type différent de ce que vous attendez, b) vous ne pouvez pas changer d'avis plus tard et essayer de mettre un entier dans un tableau, et c) il suffit de spécifier un type explicite si l'inférence de type de Swift est fausse.

Pour vous aider à démarrer, voici quelques exemples d'inférences de type :

  • var score = 0 Cela fait un Int (entier), donc la variable contient des nombres entiers.
  • var score = 0.0 Cela fait un Double, qui est l’une des nombreuses façons de contenir des nombres décimaux, par ex. 3.14159.
  • var score = "hello" Cela crée une String (chaîne de caractères), donc elle contient du texte.
  • var score = "" Même s'il n'y a pas de texte entre les guillemets, cela crée quand même une chaîne de caractères String.
  • var score = ["hello"] Cela crée un [String] avec un élément, c'est donc un tableau où chaque élément est une chaîne de caractères String.
  • var score = ["hello", "world"] Cela crée un [String] avec deux éléments, donc c'est un tableau où chaque élément est une chaîne de caractères.

Il est préférable de laisser l'inférence de type de Swift faire son travail autant que possible. Cependant, vous pouvez être explicite si vous le voulez :

  • var score: Double = 0 Swift voit le 0 donc pense que vous voulez un Int, mais nous le forçons explicitement à être un Double de toute façon.
  • var score: Float = 0.0 Swift voit le 0.0 et pense que vous voulez un Double, mais nous le forçons explicitement à devenir un Float. J'ai dit que Double est l'une des nombreuses façons de conserver des nombres décimaux, et bien Float en est une autre. En termes simples, "Double" est une forme plus précise de "Float", ce qui signifie qu’elle contient des nombres beaucoup plus grands, ou alternativement des nombres beaucoup plus précis (avec plus de chiffres après la virgule).

Nous allons mettre tout cela en pratique dans les prochaines minutes. Premièrement, remplissons notre tableau de pays avec les drapeaux que nous avons, donc ajoutons ce code dans la méthode viewDidLoad() :

countries.append("estonia")
countries.append("france")
countries.append("germany")
countries.append("ireland")
countries.append("italy")
countries.append("monaco")
countries.append("nigeria")
countries.append("poland")
countries.append("russia")
countries.append("spain")
countries.append("uk")
countries.append("us")

Ceci est identique au code que vous avez vu dans le projet 1, donc il n'y a rien à apprendre ici. Il existe un moyen plus efficace de le faire, qui consiste à tout créer sur une seule ligne. Pour ce faire, vous pouvez écrire :

countries += ["estonia", "france", "germany", "ireland", "italy", "monaco", "nigeria", "poland", "russia", "spain", "uk", "us"]

Cette ligne de code fait deux choses. Tout d'abord, elle crée un nouveau tableau contenant tous les pays. Notre tableau de pays existe déjà, et il est du type [String]. Elle utilise ensuite quelque chose de nouveau, +=. C'est ce qu'on appelle un opérateur, ce qui signifie qu'il fonctionne sur des variables et des constantes - il fait des choses avec elles. + est un opérateur, comme le sont -, *, = et bien d'autres encore. Ainsi, lorsque vous dites "5 + 4", vous avez une constante (5) un opérateur (+) et une autre constante (4).

Dans le cas de +=, il combine l'opérateur + (ajouter) et l'opérateur = (assigner) pour faire "ajouter et assigner". Traduit, cela signifie "ajoute la chose à droite à la chose qui est à gauche" ou, dans le cas de la ligne de code de nos pays, cela signifie "ajoute le nouveau groupe de pays de droite au groupe de pays existant à gauche."

Maintenant que tous les pays sont configurés, il reste encore une ligne à mettre juste avant la fin de viewDidLoad() :

askQuestion()

Elle appelle la méthode askQuestion(). Cette dernière n'existe pas encore, donc Swift va se plaindre. Cependant, elle va exister dans un instant. Cette méthode askQuestion() consistera à sélectionner des drapeaux dans le tableau, à les placer dans les boutons, puis à attendre que l'utilisateur sélectionne le bon.

Ajoutez cette nouvelle méthode sous viewDidLoad() :

func askQuestion() {
    button1.setImage(UIImage(named: countries[0]), for: .normal)
    button2.setImage(UIImage(named: countries[1]), for: .normal)
    button3.setImage(UIImage(named: countries[2]), for: .normal)
}

La première ligne est assez simple : nous déclarons une nouvelle méthode appelée askQuestion(), et elle ne prend aucun paramètre. Les trois suivantes utilisent UIImage(named:) et lisent un elément dans un tableau à partir de sa position, ce qui a déjà été utilisé dans le projet 1, donc il n'y a rien de nouveau non plus. Cependant, le reste nous montre deux nouvelles choses :

  • button1.setImage() assigne une UIImage au bouton. Le drapeau américain est déjà présent, mais cela changera lorsque askQuestion() sera appelée.
  • for: .normal. La méthode setImage() prend un second paramètre : quel état du bouton doit être changé ? Nous spécifions .normal, ce qui signifie "l'état standard du bouton".

Ce .normal cache deux complexités de plus, que vous devez comprendre. Tout d'abord, il est utilisé comme un type de données appelé "enum", l'abréviation de enumeration. Imaginez que les boutons aient trois états : normal, en surbrillance (highlighted) et désactivé (disabled). Nous pourrions représenter ces trois états comme étant 0, 1 et 2, mais ce serait difficile à programmer - est-ce que 1 correspond à désactivé ou en surbrillance ?

Les enums résolvent ce problème en nous permettant d'utiliser des noms significatifs pour certaines choses. A la place de 0, nous pouvons écrire .normal, et à la place de 1, nous pouvons écrire .disabled, etc. Cela rend le code plus facile à écrire et à lire, sans aucun impact sur les performances. Parfait !

Une remarque pour ceux qui voudraient pinailler : J'ai dit que UIControlState "est utilisé comme un type de données appelé un enum" plutôt que "est un enum", car cet exemple particulier est plutôt trouble en coulisse. En Objective-C - le langage dans lequel UIKit a été écrit - c'est une énumération, mais en Swift, il est mappé à une structure qui s'avère être utilisée comme une énumération, donc si vous voulez être techniquement correct, ce n'est pas une véritable énumération en Swift. À ce stade de votre carrière en Swift, il n’y a pas de différence, mais admettons-le : "techniquement correct" est préférable à correct.

L’autre chose que .normal cache est ce point placé au début : pourquoi est-ce .normal et pas simplement normal ? Eh bien, nous définissons l'image d'un UIButton ici, donc nous devons spécifier un état du bouton pour cela. Mais .normal peut s’appliquer à un certain nombre d’autres choses, alors comment Swift sait-il qu'il s'agit de l'état normal d'un bouton ?

Le second paramètre de setImage() est de type UIControlState, et Swift est intelligent : il s'attend à une valeur de type UIControlState à l'intérieur. Ainsi, lorsque nous écrivons .normal, il comprend que cela signifie "la valeur normal de UIControlState." Si vous le souhaitez, vous pouvez écrire la ligne en entier sous la forme UIControlState.normal, mais cela ne sert à rien.

Voici à quoi devrait ressembler votre code.

À ce stade, le jeu est prêt à fonctionner. Essayons-le.

Tout d'abord, sélectionnez le simulateur iPhone 8 en accédant au menu Product et en choisissant Destination > iPhone 8. Appuyez maintenant sur Cmd + R pour lancer le simulateur et essayer le jeu.

Vous remarquerez immédiatement deux problèmes :

  1. Nous affichons les drapeaux estonien et français, tous les deux ayant une bande blanche. Il est donc difficile de dire s'il s'agit de drapeaux ou simplement de blocs de couleur.
  2. Le "jeu" n'est pas très amusant, car ce sont toujours les mêmes trois drapeaux !

Le deuxième problème devra attendre quelques minutes, mais nous pouvons résoudre le premier maintenant. L'un des nombreux avantages des vues dans iOS est qu'elles s'appuient sur ce que l'on appelle CALayer, qui est un type de données de Core Animation, chargé de gérer l'apparence de votre vue.

Conceptuellement, CALayer se trouve sous toutes vos UIViews (c’est le parent de UIButton, UITableView, etc.), et il met à votre dispostion beaucoup d’options pour modifier l’apparence des vues, tant que cela ne vous dérange pas d'ajouter un peu de complexité. Nous allons maintenant utiliser l’une de ces options d’apparence : borderWidth.

Le drapeau estonien a une bande blanche en bas et, comme notre contrôleur de vue a un fond blanc, toute la bande est invisible. Nous pouvons résoudre ce problème en attribuant à la couche de boutons une borderWidth (largeur de bordure) de 1 point, ce qui tracera une ligne noire autour d’eux. Placez ces trois lignes dans viewDidLoad() juste avant d'appeler askQuestion() :

button1.layer.borderWidth = 1
button2.layer.borderWidth = 1
button3.layer.borderWidth = 1

Vous vous rappelez que les points et les pixels sont des choses différentes ? Dans ce cas, notre bordure sera composée d'un pixel sur les appareils non Rétina, de 2 pixels sur les appareils Rétina et de 3 pixels sur les appareils Rétina HD. Grâce à la multiplication automatique de point-à-pixel, cette bordure semblera avoir à peu près la même épaisseur sur tous les appareils.

Par défaut, la bordure de CALayer est noire, mais vous pouvez la modifier si vous le souhaitez en utilisant le type de données UIColor. J'ai dit que CALayer apportait un peu plus de complexité, et c'est là que ça commence à être visible : CALayer se situe à un niveau technique inférieur à celui de UIButton, ce qui signifie qu'il ne comprend pas ce qu'est UIColor. UIButton sait ce qu'est UIColor parce qu'ils sont tous deux au même niveau technique, mais CALayer est inférieur à UIButton, donc UIColor est un mystère.

Ne désespérez pas cependant : CALayer a sa propre méthode de définition des couleurs appelée CGColor, qui provient du framework Core Graphics d’Apple. Comme CALayer, il est à un niveau inférieur à celui de UIButton, donc les deux peuvent se parler joyeusement, mais là encore tant que vous êtes satisfait avec un peu plus de complexité.

Mieux encore, UIColor (qui se trouve au-dessus de CGColor) est capable de convertir facilement vers et depuis CGColor, ce qui signifie que vous n'avez pas à vous soucier de la complexité - hourra !

Donc, donc, donc : rassemblons tout cela dans du code qui change la couleur de la bordure en utilisant UIColor et CGColor ensemble. Placez ceci juste en-dessous des trois lignes avec borderWidth dans viewDidLoad() :

button1.layer.borderColor = UIColor.lightGray.cgColor
button2.layer.borderColor = UIColor.lightGray.cgColor
button3.layer.borderColor = UIColor.lightGray.cgColor

Comme vous pouvez le constater, UIColor a une propriété appelée lightGray qui renvoie (et bien accrochez-vous !) une instance de UIColor qui représente une couleur gris clair. Mais nous ne pouvons pas mettre de UIColor dans la propriété borderColor car elle appartient à CALayer, qui ne comprend pas ce qu'est UIColor. Donc, nous ajoutons .cgColor à la fin de UIColor pour faire automatiquement la conversion en CGColor. Parfait.

Si lightGray ne vous intéresse pas, vous pouvez créer votre propre couleur comme ceci :

UIColor(red: 1.0, green: 0.6, blue: 0.2, alpha: 1.0).cgColor

Vous devez spécifier quatre valeurs : rouge, vert, bleu et alpha, chacune d'entre elles devant être comprise entre 0 (rien de cette couleur) et 1,0 (tout de cette couleur). Le code ci-dessus génère une couleur orangée, puis la convertit en CGColor afin de pouvoir être affectée à la propriété borderColor de CALayer.

Je pense que ça suffit avec le style. Il est temps d'en faire un vrai jeu…

LEARN SWIFTUI FOR FREE I wrote a massive, free SwiftUI tutorial collection, and also have a growing list of free SwiftUI tutorials on YouTube – get started today!

< Previous: Concevoir la mise en page de votre interface utilisateur   Next: Devinez quel drapeau : nombres aléatoires >
MASTER SWIFT NOW
Buy Testing Swift Buy Practical iOS 12 Buy Pro Swift Buy Swift Design Patterns Buy Swift Coding Challenges Buy Server-Side Swift (Vapor Edition) Buy Server-Side Swift (Kitura Edition) Buy Hacking with macOS Buy Advanced iOS Volume One Buy Advanced iOS Volume Two Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with Swift Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Beyond Code

Was this page useful? Let me know!

Click here to visit the Hacking with Swift store >>