Le but de cet article est de nous familiariser avec le widget NSComboBox
. Il existe deux manières de peupler une liste déroulante : à la main dans Interface Builder, ou via l’utilisation d’une source de données. Nous allons ici nous intéresser à la seconde : l’utilisation d’une datasource.
Cet article parle de notions présentées dans ce tutoriel pour Xcode et Interface Builder. N’hésitez pas à aller vous rafraîchir la mémoire !
Dans Interface Builder, ajoutons une Combo Box dans notre vue puis, dans le premier onglet de l’inspecteur, cochons… tiens, il est marrant ce verbe conjugué au présent à la première personne du pluriel ! Hum. Cochons, donc, la case Uses Data Source.
La datasource est un objet de votre choix qui contient les données à afficher dans la liste déroulante. Cet objet retrouve et stocke les données sous la forme qu’il souhaite. Généralement, on a souvent affaire à un NSArray
qui pourrait être lu depuis un fichier plist… ou depuis les User Defaults. Bref, la seule contrainte est que cet objet doit implémenter le protocole informel NSComboBoxDataSource
:
- (NSInteger)numberOfItemsInComboBox:(NSComboBox *)combo; - (id)comboBox:(NSComboBox *)combo objectValueForItemAtIndex:(NSInteger)index |
La première méthode doit retourner le nombre d’éléments qui devront être présents dans la liste déroulante. La seconde retourne l’objet pour un index donné (le message description
sera envoyé à cet objet pour l’affichage dans la liste déroulante). Remarquez que ces méthodes acceptent toutes les deux une NSComboBox
en argument. Ceci permet de n’utiliser qu’une seule classe pour gérer plusieurs sources de données : en fonction de l’objet combo
passé en argument, il est facile de déterminer les données à utiliser.
Dans notre exemple, considérons que l’objet datasource est notre contrôleur principal, déclaré dans Interface Builder. On peut donc utiliser sa méthode awakeFromNib
pour initialiser ce qu’on veut. Ici, on va récupérer des données stockées dans les User Defaults :
- (void) loadDataSource { // On récupère une instance des User Defaults NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; // On lit les valeurs avec la clé qu'on a choisit (ici : ComboBoxValues) // dataSource est un NSArray déclaré dans le fichier header de cette classe dataSource = [[defaults arrayForKey:@"ComboBoxValues"] sortedArrayUsingSelector:@selector(compare:)]; // Au passage, on trie les éléments par ordre alphabétique :-) } -(void)awakeFromNib { // On charge nos données depuis les "préférences" de l'utilisateur [self loadDataSource]; } |
Ensuite, nous allons implémenter les deux fameuses méthodes du protocole NSComboBoxDataSource
qui sont, comme vous le constatez, très complexes :
- (int)numberOfItemsInComboBox:(NSComboBox *)aComboBox { return [dataSource count]; } - (id)comboBox:(NSComboBox *)aComboBox objectValueForItemAtIndex:(int)index { return [dataSource objectAtIndex:index]; } |
Il nous reste à indiquer à notre NSCombBox
la datasource à utiliser. Allons dans Interface Builder, sélectionnons la liste déroulante, activons l’onglet Connections de l’inspecteur puis relions l’entrée dataSource à notre contrôleur.
Très bien, mais il faut encore ajouter les données saisies dans les User Defaults, sinon l’entrée ComboBoxValues (voir la méthode loadDataSource
) sera toujours un tableau vide… Et on aurait fait tout ça pour rien ! Il faut d’abord obtenir une instance de notre liste déroulante dans notre contrôleur via l’utilisation des IBOutlet
(plus d’informations ici). Ensuite, il faut déterminer le moment où vous l’on souhaite que la valeur soit sauvegardée : lorsque que l’utilisateur termine la saisie dans la liste déroulante ou bien lors d’une autre action (sauvegarde des différents champs d’un formulaire, par exemple). Dans les deux cas, il faut déclarer une IBAction
quelque part (plus d’informations ici). Dans cette action, il nous faut ajouter un morceau de code qui ressemble à ceci :
// Récupération de la valeur saisie dans la NSComboBox // comboBox est un IBOutlet déclaré dans le fichier header (voir plus bas) NSString *newValue = [comboBox stringValue]; // On vérifie si la valeur n'est pas déjà présente, auquel cas il n'y a // rien à faire. if ( [taskNameDataSource containsObject: newValue] == NO ) { NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; // On va ajouter un élément à notre dataSource : // pour cela, il nous faut un NSMutableArray (NSArray étant en lecture seule) NSMutableArray *mutableDS = [NSMutableArray arrayWithArray: dataSource]; // On ajoute la nouvelle valeur [mutableDS addObject: newValue]; // On stocke le tout dans les User Defaults [defaults setValue: mutableDS forKey: @"ComboBoxValues"]; // Et on met à jour la liste déroulante pour qu'elle prenne la nouvelle // valeur en compte (il y a peut-être mieux à faire ici). [self loadDataSource]; } |
Enfin, voici un exemple de fichier header pour notre contrôleur principal :
@interface AppController : NSObject { IBOutlet NSComboBox* comboBox; NSArray *dataSource; } // Méthodes de gestion de la datasource pour la NSComboBox - (int)numberOfItemsInComboBox:(NSComboBox *)aComboBox; - (id)comboBox:(NSComboBox *)aComboBox objectValueForItemAtIndex:(int)index; |
Une dernière chose : il est possible de voir les préférences d’une application depuis le Terminal, grâce à la commande suivante :
defaults read identifiant.de.votre.application
L’identifiant de votre application doit être unique et on le définit dans Xcode, dans les informations de la target : dans l’arborescence de Xcode, dans la section Targets, faites un clic droit sur votre target puis Get Info et enfin onglet Properties ; le champ à renseigner est… Tah daaah : Identifier. Utilisez un nom comme les packages Java : com.entreprise.application. Attention : les defaults sont généralement synchronisés et enregistrés sur le disque à la fermeture de l’application (ou avant, mais on ne sait pas quand) : il est donc possible de ne pas les voir tout de suite à jour dans le Terminal.
Bonjour,
J’ai quelques questions sur les NSComboBox. J’ai choisi la première solution pour remplir les données C.a.d. via l’interface builder. Mais la valeur [comboBox stringValue] et le choix sur la liste affiché à l’ecran est different quand je choisi avec la sourie. C’est à dire il renvoie la valeur précédente ( comme si il y avait un retard d’un clic de sourie). Cependant quand je sélectionne un item sur la liste avec le clavier plutôt qu’avec la sourie: Ben cette fois ci la valeur et l’affichage est identique. J’utilise les Notifications pour savoir quand le choix est effectué. Tout se passe bien a part cette difference de traitement entre un choix effectué via le clavier ou une sourie.
Sinon super site bravo.
@gerald Merci pour vos compliments
Malheureusement, je n’ai pas trop le temps de me pencher sur votre problème, et ça fait bien longtemps que je n’ai plus fait de Cocoa (à mon grand regret, mais j’ai tellement de choses à faire à côté…). J’espère que vous trouverez une solution !