Oups … voici le fichier DamierView :
import SwiftUI
/// Description
/// actif : flag binaire indiquant si le DamierView doit réagir ou non à la sélection d'une case
/// damier: Liste des cases à dessiner en fonction des lignes et des colonnes
/// action: closure a exécuter quand le joueur sélectionne une case
struct DamierView: View {
var actif: Bool
var damier: Damier
var action: (_ index:IndexCase) -> Void
var body: some View {
VStack {
ForEach(0..<damier.cases.count, id: \.self) { ligne in
HStack {
ForEach(0..<self.damier.cases[ligne].count, id:\.self) { colonne in
CaseView(description: self.damier.cases[ligne][colonne])
.onTapGesture {
if self.actif {
self.action(IndexCase(ligne: ligne, colonne: colonne))
}
}
}
}
}
}
}
}
Fileprivate :
Une structure de données définie de manière globale est accessible partout dans le code. Pas terrible pour la sécurité et l’encapuslation des informations. C’est pourtant bien pratique pour simplifier l’écriture du code.
Fileprivate est une réponse à ce problème. Elle permet de définir des objets globaux, accessibles uniquement aux lignes de codes présentes dans le même fichier.
/// Dictionnaire contenant les association TypeCase-Couleur
fileprivate let couleursCase:[TypeCase:Color] = [
.vide:Color.gray,
.joueur:Color.blue,
.ia:Color.red
]
Le dictionnaire associant un type de Case avec une couleur est une structure de données globales, défini dans le fichier CaseView.swift est global, mais son attribut fileprivate fait qu’il n’existe pas en dehors du fichier. C’est le meilleur des mondes : la lisibilité d’un objet global et la sécurité d’un objet privé à utilisation local.
Static :
C’est un mot clé permettant de définir des méthodes de classe, c’est-à-dire des méthodes n’ayant pas besoin d’une instance de la classe pour fonctionner.
La manière habituelle d’utiliser une classe c’est :
a/ je crée un objet uneClasse de la classe MaClasse
b/ j’appelle une fonction pour faire quelque chose avec l’objet
var objet1 = MaClasse()
objet1.faireQuelqueChose()
On dis que faireQuelqueChose est une méthode d’instance, car elle a besoin qu’un objet de type MaClasse existe (l’instance) pour fonctionner.
Une méthode de classe n’a pas besoin d’une instance pour fonctionner. Pas besoin de créer un objet pour l’utiliser.
Si faireQuelqueChose est défini avec l’attribut Static, on peut l’utiliser directement, sans créer un objet, comme ceci :
MaClasse.faireQuelqueChose()
Swift est casse-pied avec les initialisations dans une classe ou une structure. On ne peut utiliser une variable ou une propriété utilisant Self qu’après que la classe soit entièrement définie.
Exemple :
je crée un mini générateur de couleurs aléatoire.
class GenerateurCouleurAleatoire {
private let couleurs = [Color.blue, Color.yellow, Color.green, Color.red]
func creerCouleur() -> Color {
return couleurs.randomElement()!
}
}
Cela fonctionne très bien avec ce code :
struct ContentView: View {
var generateurCouleur = GenerateurCouleurAleatoire() @State var couleur = Color.blue var body: some View { VStack { Text("Hello, World!") .font(.largeTitle) .foregroundColor(couleur) .padding() Button(action: { self.couleur = self.generateurCouleur.creerCouleur() }) { Text("Nouvelle couleur") } } }
}
A chaque pression sur le bouton, l’application génère une couleur aléatoire et modifie l’allure du texte à l’écran. Que du classique !
Au lancement de l’application, la couleur du texte est le bleu. Que se passe-t-il si si je cherche à remplacer cette couleur fixe par une couleur aléatoire ?
A priori, c’est facile à faire :
var generateurCouleur = GenerateurCouleurAleatoire()
@State var couleur = generateurCouleur.creerCouleur()
Sauf que Xcode n’est pas d’accord, hurlant à la mort !
La variable generateurCouleur contenant l’objet créé à la ligne précédente ne peut pas être utilisée comme ça, car la propriété Self définissant l’objet lui-même n’existe qu’à la fin de l’initialisation.
Cela fonctionne dans l’exemple précédent parce que le Bouton utilise la variable generateurCouleur APRES l’initialisation du ContentView, pas PENDANT cette initialisation.
La solution est de modifier le générateur de couleurs aléatoire, avec l’attribut Static pour ajouter une méthode de classe au générateur de couleur.
class GenerateurCouleurAleatoire {
static func creerCouleur() -> Color {
let couleurs = [Color.blue, Color.yellow, Color.green, Color.red]
return couleurs.randomElement()!
}
}
Petite subtilité : les méthodes de classe (static) ne peuvent accéder aux données de la classe, puisqu’elles n’ont pas étés initialisées. C’est pourquoi j’ai du transférer le tableau couleurs[] dans le corps de la fonction. Sinon, cela ne fonctionne pas.
Le programme modifié fonctionne correctement, sans avoir besoin de créer un objet générateur de couleurs.
struct ContentView: View {
@State var couleur = GenerateurCouleurAleatoire.creerCouleur() var body: some View { VStack { Text("Hello, World!") .font(.largeTitle) .foregroundColor(couleur) .padding() Button(action: { self.couleur = GenerateurCouleurAleatoire.creerCouleur() }) { Text("Nouvelle couleur") } } }
}
C’est la même logique pour la création d’un damier. La méthode de classe (static) permet de générer directement un tableau, sans passer par un objet intermédiaire et sans faire hurler Xcode à l’initialisation à cause de ce maudis Self pas encore finalisé.
Note technique : C’est là qu’on voit l’intérêt d’utiliser une lettre majuscule pour les noms de classe et une minuscule pour les objets.
@State var couleur = GenerateurCouleurAleatoire.creerCouleur()
D’un simple regard on comprend tout de suite que creerCouleur() est une méthode de classe, ne nécessitant pas d’initialisation.
La suite au prochain numéro …