Action d’un label sur différent bouton

Bonjour à tous,

Voilà si je poste ce jour un message, c’est que je n’arrive pas à ce qu’un label interagisse sur des boutons :

Concrètement voici le process que j’aimerai atteindre :

  • les boutons auront différentes données ( exemple : tableau périodique éléments).

  • l’utilisateur pourra sélectionner grâce à différents sliders qui agiront eux mêmes sur des labels (cela marche parfaitement)

  • mon problème est celui-là : l’indice incrémenté dans le label ( via slider ou clavier ) permettra une sélection de différents boutons par surbrillance ( exemple passage en couleur bleu).
    Ces boutons auront évidement une certaine valeur que je leur aurai donné.

Malheureusement je n’arrive pas à cela malgré des heures à m’arracher les cheveux.

Quelqu’un pourrait-il m’aider, je lui serai sincèrement reconnaissant.

Merci d’avance

Jean

Hello,

Je n’ai pas saisi complètement ce que tu désire faire.
Mais si je peux te donner un conseil, ne mélange pas UI et Logique métier.

Pour ton problème j’imagine que tu as des données (un tableau avec les elements périodiques et ces elements ont des attributs, masse moléculaire, etc…)

Et tu souhaites filtrer les résultats a partir d’un slider.

Il faut que tu récupères la valeur du slider. Que tu change le jeu de données (les variables) à afficher.
Et ensuite tu fais une méthode updateUI() qui va se charger UNIQUEMENT de mettre à jour l’interface utilisateur.

Pour faire simple, il ne faut pas faire un UIAction sur le slide qui effectue directement un button.text = slide.value

J’espère avoir pu aider un peu sinon n’hésite pas si tu as besoin de précisions.

Je ne comprend pas le besoin de boutons pour tes éléments. Un bouton ça sert à déclencher une action, pas à afficher une information. Un label ou une vue personnalisée avec une petite image (nom élément + symbole chimique + une image de mise en situation) ne serait pas plus approprié ?

Je t’ai fait une petite démo pour expliquer une manière possible de procéder.

J’ai 6 labels sur l’écran, représentant chacun un élément atomique. Et un Slider.

class ViewController: UIViewController {

    // Outlets vers les Labels des éléments chimiques
    @IBOutlet weak var hydrogene   : UILabel!
    @IBOutlet weak var sodium      : UILabel!
    @IBOutlet weak var fer         : UILabel!
    @IBOutlet weak var cobalt      : UILabel!
    @IBOutlet weak var cuivre      : UILabel!
    @IBOutlet weak var californium : UILabel!
    
    @IBOutlet weak var slider: UISlider!
    
    // Tableau pour mémoriser la liste des Labels/Elements
    var listeLabels = [UILabel]()
    
    // Mémorisation Label Actif
    var numeroLabelCourant = 0
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Initialisation de la liste des Labels
        listeLabels = [hydrogene, sodium, fer, cobalt, cuivre, californium]
        
        // Initialisation du Slider
        // (cela peut se faire dans le Storyboard)
        slider.minimumValue = 0
        slider.maximumValue = 1.0
        slider.value = 0
        
        numeroLabelCourant = 0
        
        actualisationInterface()
    }
    
    func actualisationInterface() {
        // Remise à 0 de la couleur de tous les Labels
        for label in listeLabels {
            label.backgroundColor = UIColor.clear
        }
        // changement de la couleur du LabelCourant
        let labelCourant = listeLabels[numeroLabelCourant]
        labelCourant.backgroundColor = UIColor.red
    }
    
    // Calcul de l'objet pointé par le Slider
    func calculObjetSlider() -> Int {
        let nombreObjets = listeLabels.count
        var objetSlider = 0
        // Le slider est-il a son extrémité droite ?
        if slider.value == 1.0 {
            objetSlider = nombreObjets - 1
        } else {
            objetSlider = Int(slider.value*Float(nombreObjets))
        }
        return objetSlider
    }
    
    // Action Slider
    @IBAction func actionSlider(_ sender: UISlider) {
        let objetSlider = calculObjetSlider()
        // Le slider pointe-t-il sur un nouveau Label ?
        if objetSlider != numeroLabelCourant {
            numeroLabelCourant = objetSlider
            actualisationInterface()
        }
    }

}

L’astuce c’est de stocker les références des Labels dans un tableau :

 // Initialisation de la liste des Labels
 listeLabels = [hydrogene, sodium, fer, cobalt, cuivre, californium]

Quand le Slider change de position il suffit de calculer à quel élément il correspond. J’ai 6 éléments dans le tableau, ce qui correspond à 6 zones différentes du Slider.

Une fois la zone déterminée (numérotée de 0 à 5), il suffit de lire le tableau pour connaître l’objet graphique concerné.

J’ai utilisé des Labels, matérialisant l’élément chimique courant avec un fond de couleur rouge. Tu peux reprendre le même principe avec n’importe quel type d’objets graphiques (des boutons, des images, etc…).

1 « J'aime »

Bonjour,

Un grand merci à vous deux pour votre aide.
Merci beaucoup Draken pour cette démo, c’est vraiment sympa de ta part d’avoir pu me consacrer un peu de ton temps.

Merci infiniment, je vais m’y mettre de suite.

Bonjour Draken,

Excusez moi de vous déranger , tout d’abord merci pour votre aide.

Je me suis un précipité dans ma réponse et votre démonstration est vraiment top mais je pense que j’ai mal exprimé mon but :
Je vais reprendre votre ViewController car il expose exactement ce que je veux faire ( avec un peu plus d’éléments mais peu importe…) :

  • imaginons que l’hydrogène est la valeur 10
  • le sodium la valeur 20
  • le fer la valeur 30
  • le cobalt la valeur 40
  • le cuivre la valeur 50
  • le californium la valeur 60

Maintenant imaginons que je veuille sélectionner les éléments qui ont une valeur <= à 30, je met donc la valeur de mon slider sur 30 et la, les éléments qui ont une valeur <= 30 se mettent en surbrillance ou change couleur de fond.

Je suis vraiment désolé de vous déranger.

Merci d’avance pour votre aide.

Amicalement

Jean

C’est ce que je dis plus haut dans mon message.
Les labels/boutons ne sont pas la pour stocker des informations. Ils sont la pour afficher de l’information.

L’information doit être ailleurs dans le code, pas sur le label.

Ok merci Gaveline,

Alors que faut il utiliser pour mon slider sélectionne des éléments (qui ont une valeur).
Et une fois que mes élément sont en surbrillance si je clique sur l’un d’eux, que cela fasse apparaître une fiche descriptive par exemple? Par contre pas dans un autre ViewController mais dans le même ViewController.

Merci d’avance pour ton aide.

J’ai une solution rapide à réaliser. C’est un peu de la magouille, mais ça marche. Comme le dis @gaveline les objets graphiques ne sont pas là pour stocker de l’information, mais pour en afficher (voir le paradigme MVC). Je vais cependant tricher un peu…

Tous les objets graphiques ont une propriété .tag permettant de leurs associer une valeur de type Int. On s’en sert généralement comme système d’identification. Il me semble que l’exercice sur la calculatrice l’utilise pour identifier la touche pressée.

Je m’en suis servis pour stocker la valeur atomique associée à chaque Label. Par exemple, avec Storyboard j’ai écrit la valeur 10 dans la propriété .tag du label Hydrogène.

Chaque label a reçu un numéro atomique.

J’ai modifié le Sider pour qu’il varie de 0 à 103 (la plus grande valeur présente dans la table des éléments périodiques).

Chaque fois que l’utilisateur modifie la position du Slider, je récupère sa valeur pour connaître la nouvelle masse atomique.

    // Action Slider
    @IBAction func actionSlider(_ sender: UISlider) {
        let masseAtomiqueSlider = Int(slider.value)
        if masseAtomiqueSlider != masseAtomiqueCourante {
            masseAtomiqueCourante = masseAtomiqueSlider
            actualisationInterface()
        }
    }

La variable globale (au contrôleur) masseAtomiqueCourante me sert à construire l’affichage.

func actualisationInterface() {
        // Remise à 0 de la couleur de tous les Labels
        for label in listeLabels {
            label.backgroundColor = UIColor.clear
        }
        
        // On boucle sur la liste des Labels pour comparer
        // leurs masses atomatiques avec la masse atomique courante
        for label in listeLabels {
            let masseAtomique = label.tag
            if masseAtomique <= masseAtomiqueCourante {
                label.backgroundColor = UIColor.red
            }
        }
    }

Je commence par remettre l’affichage dans son état initial, avec une boucle parcourant tous les Labels.

Puis j’effectue une seconde boucle pour comparer la masse atomique de chaque label avec la masseAtomique de référence du Slider. Si c’est plus petit, je change la couleur de fond en ROUGE …

Et … c’est tout … ça marche !

Code complet du viewControleur :

class ViewController: UIViewController {

    // Outlets vers les Labels des éléments chimiques
    @IBOutlet weak var hydrogene   : UILabel!
    @IBOutlet weak var sodium      : UILabel!
    @IBOutlet weak var fer         : UILabel!
    @IBOutlet weak var cobalt      : UILabel!
    @IBOutlet weak var cuivre      : UILabel!
    @IBOutlet weak var californium : UILabel!
    
    @IBOutlet weak var slider: UISlider!
    
    // Tableau pour mémoriser la liste des Labels/Elements
    var listeLabels = [UILabel]()
    
    // Mémorisation masse atomique courante
    var masseAtomiqueCourante = 0
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Initialisation de la liste des Labels
        listeLabels = [hydrogene, sodium, fer, cobalt, cuivre, californium]
        
        // Initialisation du Slider
        // (cela peut se faire dans le Storyboard)
        slider.minimumValue = 0
        slider.maximumValue = 103.0
        slider.value = 0
        
        actualisationInterface()
    }
    
    func actualisationInterface() {
        // Remise à 0 de la couleur de tous les Labels
        for label in listeLabels {
            label.backgroundColor = UIColor.clear
        }
        
        // On boucle sur la liste des Labels pour comparer
        // leurs masses atomatiques avec la masse atomique courante
        for label in listeLabels {
            let masseAtomique = label.tag
            if masseAtomique <= masseAtomiqueCourante {
                label.backgroundColor = UIColor.red
            }
        }
    }
    
    // Action Slider
    @IBAction func actionSlider(_ sender: UISlider) {
        let masseAtomiqueSlider = Int(slider.value)
        if masseAtomiqueSlider != masseAtomiqueCourante {
            masseAtomiqueCourante = masseAtomiqueSlider
            actualisationInterface()
        }
    }

}

C’est encore plus simple que l’exemple précédent.

Techniquement, ce n’est pas très propre. Un objet graphique ne devrais pas contenir une information nécessaire à l’exécution de l’application. Je ferais un autre exemple plus orthodoxe demain.

1 « J'aime »

Super, merci je vais essayer cela ce week-end, cela me semble être une bonne solution.

Merci beaucoup Draken.

Bonne journée

J’ai réalisé une version différente, basée sur une DataSource.

class ElementChimique {
    var nom : String
    var masseAtomique = 0
    var description : String
    
    init (_ nom:String, _ masseAtomique:Int, _ description:String) {
        self.nom = nom
        self.masseAtomique = masseAtomique
        self.description = description
    }
}

// Description des corps chimiques
// DataSource de l'Application
fileprivate let dataSourcesElements = [
                    ElementChimique("Hydrogène",   10, "bla bla"),
                    ElementChimique("Sodium",      20, "bla bla"),
                    ElementChimique("Fer",         30, "bla bla"),
                    ElementChimique("Cobalt",      40, "bla bla"),
                    ElementChimique("Cuivre",      50, "bla bla"),
                    ElementChimique("Californium", 60, "bla bla")
]

Cette DataSource contient la description complète des éléments chimiques. Il n’y a pas d’informations stockées ici et là dans le projet. Tout se trouve dans la dataSource, que l’on appelle aussi Modèle de données. C’est le fameux M du paradigme MVC dont on parle beaucoup en développement iOS.

Un élément est défini par son nom, sa masse atomique et une description (que je n’utilise pas, c’est juste pour montrer le principe).

Voici le code du viewController :

class ViewController: UIViewController {

    // Outlets vers les UILabels
    @IBOutlet weak var labelElement00: UILabel!
    @IBOutlet weak var labelElement01: UILabel!
    @IBOutlet weak var labelElement02: UILabel!
    @IBOutlet weak var labelElement03: UILabel!
    @IBOutlet weak var labelElement04: UILabel!
    @IBOutlet weak var labelElement05: UILabel!
    
    @IBOutlet weak var slider: UISlider!
    
    // Tableau pour mémoriser la liste des Labels/Elements
    var listeLabels = [UILabel]()
    
    // Mémorisation masse atomique courante
    var masseAtomiqueCourante = 0
    
    // Initialisation du ViewControleur
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Initialisation de la liste des Labels
        listeLabels = [
            labelElement00,
            labelElement01,
            labelElement02,
            labelElement03,
            labelElement04,
            labelElement05]
        
        // Initialisation du Slider
        // (cela peut se faire dans le Storyboard)
        slider.minimumValue = 0
        slider.maximumValue = 103.0
        slider.value = 0
        
        actualisationInterface()
    }
    
    func actualisationInterface() {
        // Enumération des Labels pour remplir les contenus
        // et remise à 0 de la couleur
        for (index, label) in listeLabels.enumerated() {
            // Récupération de l'élément Chimique correspondant au Label
            // à partir de la source de données
            let element = dataSourcesElements[index]
            let nomElement = element.nom
            // Remplissage du Label
            label.text = nomElement
            // On force le label à s'ajuster à la taille du texte
            label.sizeToFit()
            label.backgroundColor = UIColor.clear
        }
        
        // Enumération sur la liste des Labels
        // pour récupérer les masses atomiques des éléments
        for (index, label) in listeLabels.enumerated() {
            let element = dataSourcesElements[index]
            if element.masseAtomique <= masseAtomiqueCourante {
                label.backgroundColor = UIColor.red
            }
        }
    }
    
    // Action Slider
    @IBAction func actionSlider(_ sender: UISlider) {
        let masseAtomiqueSlider = Int(slider.value)
        if masseAtomiqueSlider != masseAtomiqueCourante {
            masseAtomiqueCourante = masseAtomiqueSlider
            actualisationInterface()
        }
    }

}

Il y a une totale indépendance entre la dataSource et l’interface graphique. Tu peux faire évoluer les deux indépendamment. C’est le code du viewController qui fait le lien entre les deux.

Pour qu’un élément graphique (vue, label, image, etc…), soit sensible à un événement tactile, il faut passer par Storyboard pour cocher sa propriété User Interaction Enabled.

40

C’est ce que j’ai fait avec les 6 labels de ma démo.

J’ai ensuite déposé un détecteur de Gesture sur chaque label.

6 détecteurs de gestures, c’est 6 fonctions d’actions dans le code, enfin normalement. J’ai « rusé » en créant une action pour la première gesture, puis en tirant le trait des 5 autres vers le même code.

// Action Gesture sur un Label
@IBAction func actionSelectionElement(_ sender: UITapGestureRecognizer) {
  if let tag = sender.view?.tag {
      // Identification de l'élement à partir du Tag
      let element = dataSourcesElements[tag]
      print (element.nom)
  }
}

L’astuce pour identifier le label sélectionné, c’est de passer par le .tag. J’ai donné à chaque label un tag allant de 0 à 5, comme dans l’un de mes exemples précédents. Là, il ne s’agit pas de lire une masse atomique, mais d’identifier le composant graphique sélectionné. Si tu crée une table périodique complète, avec les 103 éléments connus, tu apprécieras d’avoir une seule méthode d’action au lieu de 103 différentes … On appelle cela la « factorisation » du code.

Le tag est récupéré par l’intermédiaire du Sender. C’est un lien vers l’objet ayant provoqué l’action, c’est à dire le Détecteur de Gesture. Pour connaître l’objet graphique contenant le .tag, il faut accéder à la propriété .view de la gesture.

Petit problème technique : cette propriété est une variable optionnelle. Pour je-ne-sais-quelle-raison, iOS semble penser qu’une gesture fantôme peut provoquer un événement tactile sans être associé à une surface tactile.

Cela nous oblige à tester la validité de l’optionnelle avant de lire le tag. Le plus simple est de passer par la syntaxe if let, comme ceci :

if let tag = sender.view?.tag {
    // Identification de l'élement à partir du Tag
    let element = dataSourcesElements[tag]
    print (element.nom)
}

Je ne fais pas grande chose avec la détection du label, juste identifier l’élément et afficher son nom dans le terminal d’Xcode. A toi de reprendre le principe pour aller plus loin…

1 « J'aime »

Merci beaucoup Draken, tout marche, c’est parfait. Un grand merci pour ton aide.

A ton service. J’espère t’avoir mis sur la bonne voie. N’hésite pas à poser d’autres questions.