Demande de possibilité

Bonjour a tous,
J’ai créé un Morpion pour mon fils sur IOS, maintenant je souhaiterais rendre mon code un peu plus propre.
Est il possible d’utiliser un id de nature Integer pour des bouton ?
Je m explique dans le Morpion on a 9 cases plutôt que de les appeler Case1, Case2 etc je souhaiterais créer une variable de type integer qui me permettrait de ne jouer qu’avec un numéro plutôt que de tester les TitleLabel de chaque bouton.
Si quelqu’un a la réponse, je suis preneur.
Merci d avance

chaque composant graphique a une propriété .tag pour y associer une valeur numérique !

Salut,

Normalement dans le storyboard sur chaque bouton tu peux attribuer un « tag » de type Int et ensuite, dans ton code utiliser ce tag à partir de l’Outlet de celui-ci.

Haha mdr en même temps :joy:

Nan, moi j’ai répondu avant toi ! Moi Winner !!

1 « J'aime »

Non je refuse, c’est écrit le même temps donc, ex aequo !

Bon pour régler ce problème, on va demander à @mbritto d’afficher les secondes, voir les millisecondes, pour ne plus être dans ce cas de figure :joy:

1 « J'aime »

Tss tss … Mauvais joueur !

1 « J'aime »

Du coup par exemple Case1.settitlelabel (for : .normal )
Est ce que je peux remplacer par self.tag.settitlelabel ?

Non ! Le tag sert à identifier le composant venant d’être touché par l’utilisateur.

Pour connaitre le composant graphique associé à un tag, il faut utiliser la fonction ViewWithTag().

Mais attention, il ne faut pas que plusieurs composants de l’écran portent le même tag.

du coup si je fais un ViewWithTag() de mon tag je pourrais faire par exemple
ViewWithTag(self.tag).settitle ?

NAN !! HURLE …
JAMAIS, JAMAIS, JAMAIS !

Tu vas faire planter ton application, s’il y a une erreur de tag quelque part !
Il faut toujours contrôler des données venant de l’extérieur avant de les utiliser.

Je te tape un exemple correct, tout à l’heure. Pour te montrer comment faire …

avec le ViewWithTag je peux identifier le bouton associé ? ayant défini manuellement le tag il est compliqué de se tromper non ?

Chaque objet graphique de l’interface a une propriété .tag permettant de l’identifier. self.view est le fond de l’écran, la « feuille de papier » sur laquelle sont posée les autre objets graphiques.

ta formule :

BoutonAppuyé = self.view.tag

donneras systématiquement le tag du fond de l’écran (0 par défaut).

Pour connaître le .tag d’un objet, il faut lui demander. Cela se fait en utilisant le Sender de l’Action.

J’ai écris un mini-programme de test, très simple, un label et deux boutons portant les tags 1 et 2.

Le code :

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var monLabel: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        monLabel.text = "Aucun Tag !"
    }


    @IBAction func presserBouton(_ sender: UIButton) {
        let tag = sender.tag
        monLabel.text = "TAG : " + String(tag)
    }
}

Il n’y a qu’une seule fonction d’action, pour gérer les deux boutons.

Elle est probablement différente des tiennes à cause du paramètre Sender. C’est une information optionnelle contenant un lien vers le composant graphique ayant déclenché l’événement.

De base, Xcode génère une Action sans Sender. On peut préciser qu’il en faut un, lors de la création de l’Action. C’est ce que j’ai fait en créant l’action du premier bouton. J’ai pris un Sender de type UIButton (normal pour un bouton). Le Sender de type Any est plus compliqué d’emploi, je ne l’utilise pas.

Le second bouton pointe sur la même Action que le premier. J’ai commencé à la créer de la même manière que pour le premier bouton (Control-Clic sur le composant, puis tirer un lien graphique), mais en déplaçant l’extrémité de la ligne sur le code de l’action presserBouton avant de relâcher la souris. Xcode a compris que je voulais utiliser la même Action pour les deux boutons, et créé les liens nécessaires.

Tu peux faire la même chose avec les 9 boutons de ton code.

Comment cela fonctionne concrètement ?

    @IBAction func presserBouton(_ sender: UIButton) {
        let tag = sender.tag
        monLabel.text = "TAG : " + String(tag)
    }

A chaque pression d’un bouton, la fonction presserBouton sait de qui il s’agit grâce au Sender.

Je l’utilise pour lire le tag et l’afficher dans le Label. C’est assez simple.

Suite au prochain numéro …

si je veux utiliser les tags par exemple, J’aimerais ne plus a avoir a spécifier la case. Exemple de code case 1
BoutonAppuyé = self.view.tag
Nbredecoups = Nbredecoups + 1
Case1.setTitle(RemplirCase(casearemplir: BoutonAppuyé), for: .normal)

J’aimerais pouvoir faire au lieu de Case1.settitle, quelque chose de plus générique car j ai 9 case du coup si j ai un code et un tag ca sera plus lisible au niveau du code.

Peut on utiliser un paramètre de type settitle tel qu’utilisé sur l outlet mais sur un ViewWithTag ?

J’ai répondu à certaines de tes questions sur l’identification du bouton
pressé, plus haut …

Le Sender est un lien direct vers l’objet, exactement comme un outlet. Tu peux l’utiliser dans une Action pour modifier l’état de l’objet. Exemple :

@IBAction func presserBouton(_ sender: UIButton) {
    let tag = sender.tag
    monLabel.text = "TAG : " + String(tag)
    // Modification de la couleur de fond du bouton
    sender.backgroundColor = UIColor.red
}

C’est pas terrible comme exemple, puisque la couleur de fond du bouton reste ROUGE, même s’il est relâché.

J’en reviens à ViewWithTag, dont l’utilisation est assez délicat. C’est une fonction système générique permettant d’identifier n’importe quel objet de l’interface par son tag.

Tous les objets graphiques sont des vues (UIView). ViewWithTag recherche une vue à partir de son Tag.

Exemple pour rechercher la Vue portant le Tag 7 :

let tag = 7
var laVue = self.view.viewWithTag(tag)

Si une des vues placées sur la la vue principale self.view porte le tag 7, elle est localisé.

Mais que se passe-t-il s’il n’existe aucune vue avec le Tag 7 ? A une époque, avec l’ancien langage de programmation Apple, l’Objective-C, la fonction répondais nil, une valeur signifiant RIEN. Si aucun test de vérification n’est pratiqué, vouloir modifier l’état d’un objet qui n’existe pas, c’est le plantage automatique de l’application !

Swift intègre un système pour éviter cela, les variables optionnelles. Je ne vais pas entrer dans les détails, cela fait partie du cours de Maxime. Il me faudrais un peu plus qu’un post pour expliquer le mécanisme.

la syntaxe à utiliser pour récupérer la valeur en toute sécurité est la suivante :

    let tag = 7
    if let laVue = self.view.viewWithTag(tag) {
      // Si le tag 7 n'existe pas, Swift n'exécute pas cette portion de code
      // On peut manipuler la vue sans risque ici
      // code ...
    }

Petit problème, les vues ne sont PAS des boutons ou des Labels !!
Un Label (UILabel) est un objet sophistiqué construite à partir d’une Vue. On peut comparer ça à une maison, fabriquée avec des briques.

On peut dire à une maison, « Ouvre la porte », « combien as-tu de pièces ? », « quelle température fait-il dans la cuisine ? ». Mais pas à un tas de briques !

viewWithTag() ne peut retrouver une maison, juste un tas de briques portant un tag.

Pour utiliser un objet localisé par cette fonction, il faut le convertir en un objet d’un type précis. On appelle cela le « casting ». Cela nécessite de connaître le type d’objet à l’avance.

J’ai modifié mon mini-exemple en ajoutant une variable pour compter le nombre de fois que les boutons sont pressés. L’affichage se fait dans un nouveau Label (sans outlet), connu uniquement de l’application par son Tag (100).

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var monLabel: UILabel!
    
    var compteurBoutons = 0
    
    override func viewDidLoad() {
        super.viewDidLoad()
        monLabel.text = "Aucun Tag !"
    }

   @IBAction func presserBouton(_ sender: UIButton) {
        let tag = sender.tag
        monLabel.text = "TAG : " + String(tag)
        
        // Mise a jour du compteur
        compteurBoutons += 1
        
        // Utilisation d'un label
        // identifié par un Tag
        let numeroLabel = 100
        if let labelCompteur = self.view.viewWithTag(numeroLabel) as? UILabel {
            labelCompteur.text = "COMPTEURS BOUTONS : " + String(compteurBoutons)
        }
    
    }
}

La partie importante est :

let numeroLabel = 100
if let labelCompteur = self.view.viewWithTag(numeroLabel) as? UILabel {
    labelCompteur.text = "COMPTEURS BOUTONS : " + String(compteurBoutons)
}

La ligne de code indique à Swift de rechercher la Vue portant le Tag 100, et de vérifier qu’il s’agit bien d’un Label (UILabel). A ton stade actuel de connaissance cela fait un peu « formule magique », mais ça marche …

La variable labelCompteur est un outlet temporaire, que tu peux utiliser normalement, comme s’il avait été défini avec Storyboard, en traçant une ligne avec la souris.

Outlet temporaire, parce que cette variable n’existe qu’entre les { } du if let… Si tu en as besoin ailleurs, il faut la récréer avec viewWithTag() ou la stocker dans une variable globale du ViewControler.

Dans la pratique, on utilise rarement viewWithTag(). Les tags sont généralement utilisés pour identifier la nature d’un objet dans une Action, à partir du Sender.

Je te remercie pour toutes ces infos, cela m a vraiment été très utile. Le code du Morpion est bien plus élégant. Merci encore