C'est plutôt dans l'autre sens tant que t'as pas bien compris la POO, les bindings risquent de plus embrouiller qu'aider, car ils demandent un niveau d'abstraction supplémentaire.
Mouais, en tout cas, pour ma première petite appli, j'avais pas eu de mal à faire découler le contenu d'un tableau par rapport à un autre avec les bindings, chaque tableau utilisant une classe propre en plus.
Là , j'ai tout ds mon controlleur et ça commence à être la panique à bord.
Là , j'ai tout ds mon controlleur et ça commence à être la panique à bord.
N'hésites pas à scinder ton appli en plusieurs controleurs si tu peux. Par exemple, pour gérer un NSTabView qui comporte pas mal d'éléments, je met un contrôleur par TabItem... Tu verras, ça va tout de suite mieux avec une bonne structure...:)
Bein justement Eddy, si tu peux m'en dire plus sur la façon "d'éclater" son application... Quand tu fais plusieurs controlleurs, comment gères-tu l'import des uns vers les autres, tu n'as jamais de pb d'outlet ou autre ? Il retrouve tjs ses petits ?
Il n'y a rien que tu ne saches faire Oxitan dans le faites de faire plusieurs controleurs... Il faut dès le début bien déterminer quels sont les objets que tu vas gérer avec tel contrôleur, quelles données, quelles classes, et quels seront les liens entre les différents controleurs. Je crée un contrôleur pour chaque ensemble principal de mon interface graphique. Ensuite si tu sous-classes par exemple une NSView, et bien tu gères cette NSView avec le contrôleur qui sera responsable de la partie de l'interface graphique où réside la NSView. Une fois que tu as déterminé ceci, tu n'as plus qu'a ramener tes différents outlets et actions sur les controleurs prévus, puis gérer ton programme normalement. Le point le plus délicat à gérer est l'échange de données entre les contrôleurs. Tu peux transmettre des pointeurs sur les données soit par méthode accesseur, soit par notification. Personnellement j'ai un peu trop tendance à abuser des notifications :P, car c'est très rapide à mettre en oeuvre, mais je ne me suis pas encore interessé à une comparaison vitesse/efficacité avec les méthodes accesseurs.... Voilà , si tu as d'autres questions n'hésites pas...
OK. Les variables globales de mon controlleur principal que j'initialise à partir de plists dans le -(id)init sous forme d'array, comment je peux les récupérer dans le controlleur qui s'occupe ensuite de gérer uniquement cet array, avec tri, filtrage...etc ? Par des méthodes accesseurs dans ce controlleur secondaire (je préfère plutôt que les notifications ? J'ai du mal à visualiser le truc. ???
Remarque, je peux l'initialiser dans mon controller dédié...
Pas si fastoche la programmation par objets, hein ?
Essayons de voir les structures possibles de ces contrôleurs dans ton cas. Un des principes de base de la prog objet est l'encapsulation des données, chaque objet ne devant avoir accès qu'aux données qui lui sont strictement nécessaires
Première possiblité la structure en étoile:
C1 / C -- C2 \ C3
Ici le contrôleur principal (C), renseignera les contrôleurs auxilaires sur les données les concernant.
Une autre possiblité est une hiérarchie du type parent/enfant, ce qui est ton cas en fait:
C --> C1 --> C2 --> C3
Dans ce cas, chaque contrôleur renseignant leur contrôleur enfant.
Après tu peux écrire des accesseurs si tu le désires (pour faire comme les pros) mais le plus important est de partir d'une structure d'objets saine.
Quant aux notifications évoquées par Eddy, c'est effectivement très pratique pour permettre la communication entre deux objets n'ayant aucun lien selon la structure du programme, mais ceci doit rester l'exception, l'abus de cette technique devrait à coup sur révéler un problème de conception de base du prog.
Dans ton cas, communiquer entre contrôleurs par notification est plutôt une idée bizarre
En fait, ce matin, j'ai tout explosé. Je me retrouve avec 5 controlleurs dont un principal. Chaque controlleur a son dico et son array propres en variable d'instance et aussi ses 4 accesseurs. Le controlleur principal initialise surtout les vues et les barres d'outils. Bref, ça a l'air pas mal et pour l'instant, ça tourne comme avant. Maintenant que ma structure a l'air nettement plus propre, je vais pouvoir enfin essayer ce fameux filtrage. J'ai aussi 2 catégories pour les 2 barres d'outils et une autre pour la gestion des erreurs vers laquelle j'envoie un niveau d'erreur et un numero d'erreur. Enfin, le plus gros gros du travail est à venir avec la classe principale de mon application, une classe "DataObjects" qui va communiquer avec les 5 controlleurs. On verra...
Maintenant que ma structure a l'air nettement plus propre, je vais pouvoir enfin essayer ce fameux filtrage.
C'est bien le plus dur à comprendre dans ton prog vu de loin Est-ce le controler final, responsable de l'affichage, qui effectue ce filtrage ou est-ce le controler supérieur, si j'ai bien compris, chaque controler ne gère qu'un sous ensemble des données gérées par le controler supérieur. Dans le cas d'une structure parent/enfant, evidemment.
On pourrait aussi faire se filtrage dans l'objet Model (DataObjets ?) à la manière d'une base de données: je veux toutes les données ayant tel et tel critère.
Dans la classe controlleur de la tableView sélectionnée :
- (void)tableViewSelectionDidChange:(NSNotification *)aNotification <br />{<br /> //on prend l'id du pays de la ligne selectionnee<br /> NSDictionary* tempPaysDico = [NSMutableDictionary dictionary];<br /> NSString* tempPaysId = [NSMutableString string];<br /> tempPaysDico = [paysArray objectAtIndex: [[aNotification object] selectedRow]];<br /> tempPaysId = [tempPaysDico objectForKey:@"paysId"]; <br /> NSLog(@"tempPaysId : %@", tempPaysId);<br /> <br /> [self sendLigne: tempPaysId];<br />}
Ensuite, je voulais utiliser la même méthode que pour filtrer un tableau par recherche, avec arrangeObjects, mais en envoyant le paysId et pas une chaà®ne de recherche.
Seulement, arrangeObjects est une méthode de NSArrayController, et mon controlleur est une sous-classe de NSObject et pas de NSArrayController.
Il faudrait que je crée une classe rienq ue pour le filtrage qui est une sous-classe de ArrayController. Seulement, comme lui envoyer le numéro de ligne [self sendLigne: tempPaysId]; depuis ma classe controller ?
Une dernière petite chose. Pour ma notification, enfin mon delegate selectionDidChange, quand je clique dans le tableau mais pas sur une ligne, ça me renvoie ça :
Exception raised during posting of notification. Ignored. exception: *** -[NSCFArray objectAtIndex:]: index (-1) beyond bounds (2)
J'ai essayé de rajouter cela sans succès : if (![[aNotification object] selectedRow]) { code }
ou encore cela : if (![[aNotification object] selectedRow] != -1) { code }
mais j'ai tjs le message, je veux simplement lui dire de rien faire si aucune ligne n'est sélectionnée...
mouais, ça veut dire qu'il faut encore demander quelle ligne est sélectionnée... je pensais qu'il y avait un truc du genre : [maTable déselectionne toutes leslignes];
Ben, mon p'tit Alex, que vois je dans la doc Apple sur NSTableView ?
deselectAll:
- (void)deselectAll:(id)sender
Deselects all selected rows or columns if empty selection is allowed; otherwise does nothing. Posts NSTableViewSelectionDidChangeNotification to the default notification center if the selection does in fact change. As a target-action method, deselectAll: checks with the delegate before changing the selection, using selectionShouldChangeInTableView:.
See Also: – allowsEmptySelection, – selectAll:, – selectColumn:byExtendingSelection:
Bon, mon filtrage de tableView et bien... il va falloir faire la même chose ailleurs sur des popup. Un popup alimente un autre popup suivant sa sélection. Gloups. Je compte me servir de addItemsWithTitles avec un array pour l'alimenter. La chose que je me demande est la suivante : dans un popupButton, on met une liste de strings mais pas des dico avec 2 clés par exemple, or moi je veux que mon nouvel enregistrement enregitre l'id correspondant à la selection dans le popup et pas la string qui s'affiche. Je vois pas trop comment m'y prendre à part en comparant la string du popup à celle de mon array de dico qui contient aussi cette string en plus de la key Id pour ensuite insérer cette key Id... Suis-je clair ?
Ce message est plus rassurant qu'utile : apparemment il n'y a pas d'astuce, on ne peut effectivement que stocker des NSString dans un popup. Du coup, oui, il faut à la main faire le lien avec un objet particulier.
Dans la doc sur les popup, il y a l'exemple suivant, où l'on voit bien que c'est la galère pour identifier un objet en fonction de la sélection:
- (void)setLanguage:(id)sender{ NSString *title = [languagePopUp titleOfSelectedItem]; if ([title isEqualToString:@English]) language = English; else if ([title isEqualToString:@French]) language = French; else if ([title isEqualToString:@German]) language = German; else if ([title isEqualToString:@Spanish]) language = Spanish; else if ([title isEqualToString:@Swedish]) language = Swedish; }
mpergand a raison ! j'ai essayé de faire un petit exemple : une fenêtre qui contient un bouton popup, et une classe dans mon NIB qui connaà®t le popup sous le nom "popup" Le code suivant est tout à fait convenable
//quand on change la selection du popup -(IBAction) changeSelection:(id) sender { id selected = [sender selectedItem]; //renvoie un NSMenuItem id object = [selected representedObject]; //le fameux representedObject int value = [object intValue]; //bon là je vais afficher sa valeur NSLog(@selected = %d, value); //affiche bien l'int voulu ! }
-(void) awakeFromNib { NSLog(@awake from nib); //je créee trois objets NSNumber* one = [[NSNumber alloc] initWithInt:1]; NSNumber* two = [[NSNumber alloc] initWithInt:2]; NSNumber* three = [[NSNumber alloc] initWithInt:3]; NSLog(@one = %x, one); NSLog(@two = %x, two); NSLog(@three = %x, three);
Oui en effet c'est un peu sur ce schéma que sont également conçus les bindings avec la possibilité de binder le content ou le content value, le selected object ou le selected value
Réponses
Dommage donc que les deux exemples utilisent les bindings...
J'aimerais bien voir un filtrage sur des arrays sans rearrangedObjects qui est réservé aux bindings apparemment.
Ceci dit, pour les prefs, tu peux les utiliser
Là , j'ai tout ds mon controlleur et ça commence à être la panique à bord.
N'hésites pas à scinder ton appli en plusieurs controleurs si tu peux. Par exemple, pour gérer un NSTabView qui comporte pas mal d'éléments, je met un contrôleur par TabItem... Tu verras, ça va tout de suite mieux avec une bonne structure...:)
Quand tu fais plusieurs controlleurs, comment gères-tu l'import des uns vers les autres, tu n'as jamais de pb d'outlet ou autre ? Il retrouve tjs ses petits ?
Il faut dès le début bien déterminer quels sont les objets que tu vas gérer avec tel contrôleur, quelles données, quelles classes, et quels seront les liens entre les différents controleurs.
Je crée un contrôleur pour chaque ensemble principal de mon interface graphique. Ensuite si tu sous-classes par exemple une NSView, et bien tu gères cette NSView avec le contrôleur qui sera responsable de la partie de l'interface graphique où réside la NSView.
Une fois que tu as déterminé ceci, tu n'as plus qu'a ramener tes différents outlets et actions sur les controleurs prévus, puis gérer ton programme normalement. Le point le plus délicat à gérer est l'échange de données entre les contrôleurs. Tu peux transmettre des pointeurs sur les données soit par méthode accesseur, soit par notification. Personnellement j'ai un peu trop tendance à abuser des notifications :P, car c'est très rapide à mettre en oeuvre, mais je ne me suis pas encore interessé à une comparaison vitesse/efficacité avec les méthodes accesseurs....
Voilà , si tu as d'autres questions n'hésites pas...
Les variables globales de mon controlleur principal que j'initialise à partir de plists dans le -(id)init sous forme d'array, comment je peux les récupérer dans le controlleur qui s'occupe ensuite de gérer uniquement cet array, avec tri, filtrage...etc ? Par des méthodes accesseurs dans ce controlleur secondaire (je préfère plutôt que les notifications ? J'ai du mal à visualiser le truc. ???
Remarque, je peux l'initialiser dans mon controller dédié...
Essayons de voir les structures possibles de ces contrôleurs dans ton cas.
Un des principes de base de la prog objet est l'encapsulation des données, chaque objet ne devant avoir accès qu'aux données qui lui sont strictement nécessaires
Première possiblité la structure en étoile:
C1
/
C -- C2
\
C3
Ici le contrôleur principal (C), renseignera les contrôleurs auxilaires sur les données les concernant.
Une autre possiblité est une hiérarchie du type parent/enfant, ce qui est ton cas en fait:
C --> C1 --> C2 --> C3
Dans ce cas, chaque contrôleur renseignant leur contrôleur enfant.
Après tu peux écrire des accesseurs si tu le désires (pour faire comme les pros) mais le plus important est de partir d'une structure d'objets saine.
Quant aux notifications évoquées par Eddy, c'est effectivement très pratique pour permettre la communication entre deux objets n'ayant aucun lien selon la structure du programme, mais ceci doit rester l'exception, l'abus de cette technique devrait à coup sur révéler un problème de conception de base du prog.
Dans ton cas, communiquer entre contrôleurs par notification est plutôt une idée bizarre
En fait, ce matin, j'ai tout explosé. Je me retrouve avec 5 controlleurs dont un principal. Chaque controlleur a son dico et son array propres en variable d'instance et aussi ses 4 accesseurs. Le controlleur principal initialise surtout les vues et les barres d'outils. Bref, ça a l'air pas mal et pour l'instant, ça tourne comme avant. Maintenant que ma structure a l'air nettement plus propre, je vais pouvoir enfin essayer ce fameux filtrage.
J'ai aussi 2 catégories pour les 2 barres d'outils et une autre pour la gestion des erreurs vers laquelle j'envoie un niveau d'erreur et un numero d'erreur.
Enfin, le plus gros gros du travail est à venir avec la classe principale de mon application, une classe "DataObjects" qui va communiquer avec les 5 controlleurs. On verra...
C'est bien le plus dur à comprendre dans ton prog vu de loin
Est-ce le controler final, responsable de l'affichage, qui effectue ce filtrage ou est-ce le controler supérieur, si j'ai bien compris, chaque controler ne gère qu'un sous ensemble des données gérées par le controler supérieur. Dans le cas d'une structure parent/enfant, evidemment.
On pourrait aussi faire se filtrage dans l'objet Model (DataObjets ?) à la manière d'une base de données: je veux toutes les données ayant tel et tel critère.
Voilà comment je voulais procéder.
Dans la classe controlleur de la tableView sélectionnée :
Ensuite, je voulais utiliser la même méthode que pour filtrer un tableau par recherche, avec arrangeObjects, mais en envoyant le paysId et pas une chaà®ne de recherche.
Seulement, arrangeObjects est une méthode de NSArrayController, et mon controlleur est une sous-classe de NSObject et pas de NSArrayController.
Il faudrait que je crée une classe rienq ue pour le filtrage qui est une sous-classe de ArrayController. Seulement, comme lui envoyer le numéro de ligne [self sendLigne: tempPaysId]; depuis ma classe controller ?
Reste que rearrangeObjects n'a pas l'air d'appeler arrangeObjects.
Alors dans le controlleur pays :
On envoie le paysId au controlleur de régions.
Et dans le controlleur de régions, on reconstruit le tableau filtré :
Et voilà , ça marche nikel mon filtrage. :spot:
Pas besoin de s'embêter avec NSArrayController...
Pour ma notification, enfin mon delegate selectionDidChange, quand je clique dans le tableau mais pas sur une ligne, ça me renvoie ça :
Exception raised during posting of notification. Ignored. exception: *** -[NSCFArray objectAtIndex:]: index (-1) beyond bounds (2)
J'ai essayé de rajouter cela sans succès :
if (![[aNotification object] selectedRow]) { code }
ou encore cela :
if (![[aNotification object] selectedRow] != -1) { code }
mais j'ai tjs le message, je veux simplement lui dire de rien faire si aucune ligne n'est sélectionnée...
marchait bien... je sais pas ce que j'avais bidouillé.
Une idée ?
EDIT :
[regionsTable deselectRow: [regionsTable selectedRow]];
ça marche.
Le lien, c'est ici.
.
Tient me voilà "artisan chocolatier".
Soit tu fais ce réglage dans IB (dans la palette), soit tu utilises la méthode [tt]- (void)setAllowsEmptySelection:(BOOL)flag[/tt] de NSTableView.
.
[regionsTable deselectAll: self];
nikel chrome !
Oui, oui, allowsEmptySelection est activé.
Bon, mon filtrage de tableView et bien... il va falloir faire la même chose ailleurs sur des popup. Un popup alimente un autre popup suivant sa sélection. Gloups.
Je compte me servir de addItemsWithTitles avec un array pour l'alimenter. La chose que je me demande est la suivante : dans un popupButton, on met une liste de strings mais pas des dico avec 2 clés par exemple, or moi je veux que mon nouvel enregistrement enregitre l'id correspondant à la selection dans le popup et pas la string qui s'affiche.
Je vois pas trop comment m'y prendre à part en comparant la string du popup à celle de mon array de dico qui contient aussi cette string en plus de la key Id pour ensuite insérer cette key Id... Suis-je clair ?
Dans la doc sur les popup, il y a l'exemple suivant, où l'on voit bien que c'est la galère pour identifier un objet en fonction de la sélection:
- (void)setLanguage:(id)sender{
NSString *title = [languagePopUp titleOfSelectedItem];
if ([title isEqualToString:@English])
language = English;
else if ([title isEqualToString:@French])
language = French;
else if ([title isEqualToString:@German])
language = German;
else if ([title isEqualToString:@Spanish])
language = Spanish;
else if ([title isEqualToString:@Swedish])
language = Swedish;
}
j'ai essayé de faire un petit exemple : une fenêtre qui contient un bouton popup, et une classe dans mon NIB qui connaà®t le popup sous le nom "popup"
Le code suivant est tout à fait convenable
//quand on change la selection du popup
-(IBAction) changeSelection:(id) sender
{
id selected = [sender selectedItem]; //renvoie un NSMenuItem
id object = [selected representedObject]; //le fameux representedObject
int value = [object intValue]; //bon là je vais afficher sa valeur
NSLog(@selected = %d, value); //affiche bien l'int voulu !
}
-(void) awakeFromNib
{
NSLog(@awake from nib);
//je créee trois objets
NSNumber* one = [[NSNumber alloc] initWithInt:1];
NSNumber* two = [[NSNumber alloc] initWithInt:2];
NSNumber* three = [[NSNumber alloc] initWithInt:3];
NSLog(@one = %x, one);
NSLog(@two = %x, two);
NSLog(@three = %x, three);
//je remplis mon popup
[popup removeAllItems];
[popup addItemWithTitle:@one];
[[popup itemAtIndex:0] setRepresentedObject:one];
[popup addItemWithTitle:@two];
[[popup itemAtIndex:1] setRepresentedObject:two];
[popup addItemWithTitle:@three];
[[popup itemAtIndex:2] setRepresentedObject:three];
}
Et ça marche!
c'est un peu sur ce schéma que sont également conçus les bindings avec la possibilité de binder le content ou le content value, le selected object ou le selected value
J'espère juste que ça demandera pas trop de code.
Merci en tout cas mpergand et chacha pour cette info.
[addDico setObject: [newOriginePopup indexOfSelectedItem] forKey:@origine];
me renvoie un warning : make pointer without a cast
[addDico setObject: [[newOriginePopup indexOfSelectedItem] intValue] forKey:@origine];
encore pire...
en fait, je voudrais mettre le chiffre de l'index dans <string>1</string dans mon dico....