NSDictionary et NSMutableArray

RocouRocou Membre
juin 2008 modifié dans API AppKit #1
Je n'arrive pas à  extraire des données précises d'un objet NSMutableArray ou NSDictionary.

Je tente d'accéder à  une base de données PostgreSQL avec le Framework PGSQLKit. ça fonctionne, voici le code:
#import &quot;CollecteControl.h&quot;<br />#import &quot;/Library/Frameworks/PGSQLKit.framework/Versions/A/Headers/PGSQLConnection.h&quot;<br /><br />//-(NSDictionary *)dictionaryFromRecord; ***** juste pour rappel<br /><br />@implementation CollecteControl<br />- (IBAction)a_recapclper:(id)sender<br />{<br />&nbsp; NSString *user = @&quot;postgres&quot;;<br />&nbsp; &nbsp;NSString *password = @&quot;&quot;;<br />&nbsp; &nbsp;<br />&nbsp; &nbsp;NSString *serverName = @&quot;localhost&quot;;<br />&nbsp; &nbsp;NSString *serverPort = @&quot;5432&quot;;<br />&nbsp; &nbsp;NSString *databaseName = @&quot;sxcollecte&quot;;<br />&nbsp; &nbsp;<br />&nbsp; &nbsp;PGSQLConnection *connection = [[PGSQLConnection alloc] init];<br />&nbsp; &nbsp;<br />&nbsp; &nbsp;[connection setUserName:user];<br />&nbsp; &nbsp;[connection setPassword:password];<br />&nbsp; &nbsp;<br />&nbsp; &nbsp;[connection setServer:serverName];<br />&nbsp; &nbsp;[connection setPort:serverPort];<br />&nbsp; &nbsp;[connection setDatabaseName:databaseName];<br />&nbsp; &nbsp;<br />&nbsp; &nbsp;if ([connection connect])<br />&nbsp; &nbsp;{<br />&nbsp; &nbsp;NSLog(@&quot;connexion ok&quot;);<br /><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;NSString *query = @&quot;select clients.nom as client from clients&quot;;<br />			 //&nbsp; &nbsp; query=&quot;SELECT clients.nom as client, conteneurs.volume, dechets.nature, COUNT(conteneurs.volume) as nbBacs FROM &quot;&quot;prestations&quot;&quot; INNER JOIN clients ON prestations.client_id = clients.id INNER JOIN conteneurs ON prestations.conteneur_id = conteneurs.id INNER JOIN tournees ON prestations.tournee_id=tournees.id INNER JOIN dechets ON tournees.dechet_id=dechets.id INNER JOIN collaborateurs ON collaborateurs.id=clients.facturation_id &quot;+ &quot;AND &quot; + &quot;clients.nom=&quot; +&quot;&#39;&quot;+ PopupMenu1.text+&quot;&#39;&quot; + &quot; WHERE prestations.ladate &gt;= &quot;+ &quot;&#39;&quot;+ dstockdebut + &quot;&#39;&quot; + &quot; AND prestations.ladate &lt;= &quot;+&quot;&#39;&quot;+ dstockfin + &quot;&#39; &quot;+ &quot;GROUP BY clients.nom, conteneurs.volume, dechets.nature&quot;<br />&nbsp; &nbsp; <br />			 NSMutableArray *result = nil;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;		&nbsp; &nbsp;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;PGSQLRecordset *rs = [connection open:query];<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if (rs != nil) {<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if (![rs isEOF])<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;{<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if (result != nil)<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;{<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;[result release];<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;result = nil;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;result = [[NSMutableArray alloc] init];<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;while (![rs isEOF])<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;{<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;[result addObject:[rs dictionaryFromRecord]];<br />					&nbsp; &nbsp;NSLog(@&quot;result: %@&quot;, result);<br />					&nbsp; &nbsp;<br /><br /><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;[rs moveNext];<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;[rs close];&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;[result release];<br />&nbsp; &nbsp; &nbsp; &nbsp;}<br />&nbsp; &nbsp; &nbsp; &nbsp;<br />&nbsp; &nbsp; &nbsp; &nbsp;[connection close];<br />&nbsp; &nbsp; &nbsp; &nbsp;<br />&nbsp; &nbsp; &nbsp; // return result;&nbsp; &nbsp; &nbsp; &nbsp; <br />&nbsp; &nbsp;} else {<br />&nbsp; &nbsp; &nbsp; &nbsp;// setup the error dictionary<br />			NSLog(@&quot;Impossible de se connecter à  la base de données&quot;);<br />		[self performSelector:@selector(getConnectionWithLogin) withObject:nil afterDelay:0.0];	<br />		return;<br />	<br />&nbsp; &nbsp;}<br />&nbsp; &nbsp;return nil;&nbsp; &nbsp; <br />&nbsp; } <br /><br />@end<br />


Le résultat affiché sur la console est le suivant:
result: (
        {
        client = "Client1";
    }
)
result: (
        {
        client = "Client1";
    },
        {
        client = "Client2";
    }
)
...
Et ainsi de suite jusqu'à  la fin de la requete.
Comment faire pour remplir un objet "Table View" avec cela? Faut-il se débarrasser des parenthèses, des accolades et du "client=" avant? Si oui comment faire?
J'ai cru comprendre qu'un NSDictionary ou un NSMutableArray est un tableau. J'ai essayé d'afficher result[1] ou result[1,1] mais le compilateur n'a pas l'air d'apprécier.
«13

Réponses

  • AliGatorAliGator Membre, Modérateur
    16:40 modifié #2
    Salut,

    Alors NSDictionary et NSMutableDictionary sont un peu des sortes de tableau si tu veux (quoique c'est plutôt NS(Mutable)Array qui sont des tableaux), mais ça reste des objets Cocoa. Il faut donc accéder à  leurs éléments en utilisant les méthodes de la classe NS(Mutable)Dictionary, et non pas avec une syntaxe à  la C avec des crochets, car ce ne sont pas des tableaux C pour autant.

    1) NSMutableArray dérive de NSArray (c'est jsute un NSArray à  qui est rajouté la possibilité d'être modifiable, en somme), et pour accéder à  ses éléments, tu as la méthode objectAtIndex:

    2) NSDictionary est un dictionnaire de données, c'est à  dire un ensemble de paires { clé = valeur } (key/value pairs).
    Dans ton cas, à  chaque enregistrement (record) résultat de ta requête PostgreSQL correspond un dictionnaire, dont les clés sont les noms des champs de ta table et les valeurs... ben la valeur de ce champ pour cet enregistrement. Et il se trouve que d'après ton exemple, chacun de tes dictionnaires (que tu mets dans ton NSMutableArray) n'a qu'une seul clé, nommée "client".

    3) Ce que tu vois dans la console n'est qu'une représentation textuelle du contenu de l'objet. Ce n'est pas comme ça explicitement que l'objet est stocké : c'est juste qu'avec la commande NSLog tu lui as demandé de t'afficher sur la console l'objet, et pour ça il faut bien qu'il t'en sorte une représentation textuelle juste pour l'affichage dans la console. Mais dans ton programme ça reste un objet Cocoa, à  manipuler avec les méthodes qui vont bien.


    Par exemple pour récupérer la valeur de la clé "client" du 2e élément de ton NSMutableArray (autrement dit la valeur du champ "client" de ton 2e enregistrement retourné par ta requête PostgreSQL), il suffit de faire :
    NSString* valeur = [[result objectAtIndex:1] valueForKey:@&quot;client&quot;];
    

    Ou encore si tu veux décomposer :
    NSDictionary* fiche_2 = [result objectAtIndex:1]; // le 1er c&#39;est 0, le 2e c&#39;est 1<br />NSString* client_de_fiche_2 = [fiche_2 valueForKey:@&quot;client&quot;];
    

  • RocouRocou Membre
    16:40 modifié #3
    Merci, c'est enfin clair!

    Il manque un tutorial à -la-tablier (ie pour débutant total) concernant objective-c. S'il y a cela sur le net, je suis preneur...
  • AliGatorAliGator Membre, Modérateur
    16:40 modifié #4
    De rien ;)

    j'en remet une couche : je ne vais pas te faire un tuto (le temps où j'en faisais est révolu ;)) mais juste t'orienter aussi sur NSEnumerator qui sont faites pour parcourir des classes de type "collection" (comme NSArray, NSDictionary, ... classes faites pour contenir des objets quoi) facilement.
    Ainsi, une fois que tu as ton tableau "result", si tu veux le parcourir dans son ensemble, tu peux faire comme ceci (là  je demande d'afficher dans la console le client de chaque fiche) :
    NSEnumerator* e = [result objectEnumerator];<br />NSDictionary* fiche = nil;<br />while( (fiche = [e nextObject]) != nil )<br />{<br />&nbsp; NSLog(@&quot;Cette fiche a %@ pour client.&quot; , [fiche valueForKey: @&quot;client&quot;]);<br />}
    
    C'est un code équivalent à  :
    <br />int i , n = [result count];<br />NSDictionary* fiche = nil;<br />for(i=0; i&lt;n ; i++)<br />{<br />&nbsp; NSLog(@&quot;La fiche n°%d a %@ pour client.&quot; , i, [[result objectAtIndex:i] valueForKey: @&quot;client&quot;]);<br />}
    
  • RocouRocou Membre
    16:40 modifié #5
    dans 1213100950:


    Ainsi, une fois que tu as ton tableau "result", si tu veux le parcourir dans son ensemble, tu peux faire comme ceci (là  je demande d'afficher dans la console le client de chaque fiche) :
    NSEnumerator* e = [result objectEnumerator];<br />NSDictionary* fiche = nil;<br />while( (fiche = [e nextObject]) != nil )<br />{<br />&nbsp; NSLog(@&quot;Cette fiche a %@ pour client.&quot; , [fiche valueForKey: @&quot;client&quot;]);<br />}
    


    Merci pour ces précisions.
    N'y-a-t-il pas une erreur de syntaxe dans le NSLog? Après test, "pour client" n'est pas affiché.
  • RocouRocou Membre
    16:40 modifié #6
    dans 1213100950:

    De rien ;)

    j'en remet une couche : je ne vais pas te faire un tuto (le temps où j'en faisais est révolu ;)) mais juste t'orienter aussi sur NSEnumerator qui sont faites pour parcourir des classes de type "collection" (comme NSArray, NSDictionary, ... classes faites pour contenir des objets quoi) facilement.
    Ainsi, une fois que tu as ton tableau "result" ...

    A propos, comment afficher "result" dans un "tableview"?
    J'ai bien essayé ceci:
    [o_table setTableValue:result];<br />
    

    mais cela ne fonctionne pas du tout. En outre la doc dit qu'on ne devrait jamais avoir à  utiliser la méthode setTableValue.
    Dois-je initialiser toutes les cellules de la table les unes après les autres après avoir déterminé le nombre de colonnes nécessaires? Mais dans ce cas, faut-il créer des outlets dynamiquement? (un par cellule?)
  • Philippe49Philippe49 Membre
    juin 2008 modifié #7
    dans 1213105566:

    A propos, comment afficher "result" dans un "tableview"?
    Dois-je initialiser toutes les cellules de la table les unes après les autres après avoir déterminé le nombre de colonnes nécessaires?


    Using Table View data Source

    Dans IB associe l'outlet datasource de ta table view à  l'instance de CollecteControl, et implémente les méthodes du protocole NSTableDataSource.
    Il y a un modèle dans Cocoa par la pratique.



  • Philippe49Philippe49 Membre
    16:40 modifié #8
    dans 1213100950:

    NSEnumerator* e = [result objectEnumerator];<br />NSDictionary* fiche = nil;<br />while( (fiche = [e nextObject]) != nil )<br />{<br />&nbsp; NSLog(@&quot;Cette fiche a %@ pour client.&quot; , [fiche valueForKey: @&quot;client&quot;]);<br />}
    


    Ou encore depuis Objective C 2.0

    for(NSDictionary * fiche in result){
      NSLog(@Cette fiche a %@ pour client." , [fiche valueForKey: @client]);
    }
  • RocouRocou Membre
    16:40 modifié #9
    dans 1213106379:

    Using Table View data Source

    Dans IB associe l'outlet datasource de ta table view à  l'instance de CollecteControl, et implémente les méthodes du protocole NSTableDataSource.
    Il y a un modèle dans Cocoa par la pratique.


    Oui mais ça ne fonctionne pas chez moi. J'ai même récupéré un "project" dont tu es l'auteur (http://www.objective-cocoa.org/forum/index.php?action=dlattach;topic=2692.0;attach=2048). J'ai copié-collé tout ce qui pouvait l'être mais rien ne s'affiche dans ma tableview.
    Je n'arrive pas à  comprendre comment est "reconnue" la tablewiew puisqu'il n'y a pas d'outlet.


  • Philippe49Philippe49 Membre
    juin 2008 modifié #10
    En général, on utilise deux connections, seule la connection "data source" est nécessaire.

    Instancier un  CollecteControler dans le MainMenu.nib à  l'aide d'un "cube bleu"
    Clic-droit sur la table view, connecter Dataspurce au CollecteController
    CollecteController peut définir un IBOutlet NSTableView * tableView;
    Connecter cet outlet à  la tableview dans le nib

    Si cela ne marche pas, c'est le code qui coince, envoie-le (sans le build pour alléger le poids)   on peut regarder


  • Philippe49Philippe49 Membre
    juin 2008 modifié #11
    dans 1213272868:

    Je n'arrive pas à  comprendre comment est "reconnue" la tablewiew puisqu'il n'y a pas d'outlet.

    L'outlet tableView n'est pas nécessaire, car dans les requêtes au datasource, la tableview se déclare :
    Par exemple, la méthode
    - (id)tableView:(NSTableView *)aTableView
        objectValueForTableColumn:(NSTableColumn *)aTableColumn
        row:(int)rowIndex
    dit quelle tableview demande de l'info ...
    une même (instance) classe peut donc être datasource pour plusieurs table view.

  • RocouRocou Membre
    16:40 modifié #12
    dans 1213273996:

    dans 1213272868:

    Je n'arrive pas à  comprendre comment est "reconnue" la tablewiew puisqu'il n'y a pas d'outlet.

    L'outlet tableView n'est pas nécessaire, car dans les requêtes au datasource, la tableview se déclare :
    Par exemple, la méthode
    - (id)tableView:(NSTableView *)aTableView
        objectValueForTableColumn:(NSTableColumn *)aTableColumn
        row:(int)rowIndex
    dit quelle tableview demande de l'info ...
    une même classe peut donc être datasource pour plusieurs table view.



    Justement, c'est cela que je ne comprends pas: où voit-on quelle tableview demande l'info?
  • ChachaChacha Membre
    16:40 modifié #13

    - (id)tableView:(NSTableView *)aTableView
        objectValueForTableColumn:(NSTableColumn *)aTableColumn
        row:(int)rowIndex


    Si un objet A est défini comme étant le datasource d'une tableview T ([T setDataSource:A]), alors, quand T a besoin des données, elle appelle la méthode tableView:objectValueForTableColumn:row:.
    Or, comme tu le dis, il faut bien qu'elle s'identifie (car A pourrait être fournisseurs de plusieurs tableViews). C'est tout simple : la tableview qui demande est celle qui est passé en paramètre à  la méthode !
    Exemple :
    <br />@implementation A<br />...<br />-(id) tableView:(NSTableView*)aTableView objectValueForTableColumn:(NSTableColumn*)aTableColumn<br />&nbsp; &nbsp; row:(int)rowIndex<br />{<br />&nbsp; if (aTableView == T)<br />&nbsp; {<br />&nbsp; &nbsp; &nbsp;//c&#39;est T qui réclame des données<br />&nbsp; }<br />}<br />...<br />@end<br />
    


    +
    Chacha
  • RocouRocou Membre
    16:40 modifié #14
    dans 1213276948:


    - (id)tableView:(NSTableView *)aTableView
        objectValueForTableColumn:(NSTableColumn *)aTableColumn
        row:(int)rowIndex


    Si un objet A est défini comme étant le datasource d'une tableview T ([T setDataSource:A]), alors, quand T a besoin des données, elle appelle la méthode tableView:objectValueForTableColumn:row:.
    Or, comme tu le dis, il faut bien qu'elle s'identifie (car A pourrait être fournisseurs de plusieurs tableViews). C'est tout simple : la tableview qui demande est celle qui est passé en paramètre à  la méthode !
    Exemple :
    <br />@implementation A<br />...<br />-(id) tableView:(NSTableView*)aTableView objectValueForTableColumn:(NSTableColumn*)aTableColumn<br />&nbsp; &nbsp; row:(int)rowIndex<br />{<br />&nbsp; if (aTableView == T)<br />&nbsp; {<br />&nbsp; &nbsp; &nbsp;//c&#39;est T qui réclame des données<br />&nbsp; }<br />}<br />...<br />@end<br />
    


    +
    Chacha

    D'accord mais comment faire pour identifier la tableview par T?
    J'ai essayé de définir un outlet T, j'ai regardé s'il avait une zone "titre" dans "l'inspector" de la tableview. Je sèche.

    Par ailleurs, dans ton exemple, que doit on mettre à  la place du commentaire pour remplir la tableview?
    if (aTableView == T)<br />&nbsp; {<br />&nbsp; &nbsp;  //c&#39;est T qui réclame des données. oui mais est ce ici que l&#39;on envoie les données à  la tableview? <br />&nbsp; &nbsp;  //Et comment?<br />&nbsp; }
    

  • Philippe49Philippe49 Membre
    16:40 modifié #15
    dans 1213274940:

    Justement, c'est cela que je ne comprends pas: où voit-on quelle tableview demande l'info?

    Si il n'y a qu'une table view, le problème ne se pose pas ...
    Sinon, et si on renâcle à  mettre un outlet dans le controller, on peut comme avec tous les nscontrol attribuer une valeur de tag différente à  chaque tableview

    enum {TABLEVIEWACCOUNTS=0,TABLEVIEWSAVINGS=1};

    -(id) tableView:(NSTableView*)aTableView objectValueForTableColumn:(NSTableColumn*)aTableColumn
       row:(int)rowIndex
    {
     if ([aTableView tag]== TABLEVIEWACCOUNTS)
     {
        //c'est T qui réclame des données
     }
    }

  • Philippe49Philippe49 Membre
    juin 2008 modifié #16
    dans 1213282939:

    D'accord mais comment faire pour identifier la tableview par T?
    J'ai essayé de définir un outlet T, j'ai regardé s'il avait une zone "titre" dans "l'inspector" de la tableview. Je sèche.

    Le fait de définir et connecter un outlet fait que T pointe sur l'adresse mémoire où est stockée la table view.
    D'autre part la valeur envoyée dans la méthode est également cette adresse.
    Donc le test que t'indique Chacha aTableView == T compare l'adresse de la table view qui demande l'info à  l'adresse référencée par l'outlet.

    dans 1213282939:

    Par ailleurs, dans ton exemple, que doit on mettre à  la place du commentaire pour remplir la tableview?

    Si tu veux qu'à  cette ligne (row) et colonne soit écrit "toto" , tu renvoies
    return @toto

    En général, on crée une NSArray d'objets, la "row" correspond à  l'indice dans la NSArray, et l'objet à  un des champs de l'objet (ou du dictionnaire) de la NSArray.
  • ChachaChacha Membre
    juin 2008 modifié #17
    dans 1213282939:

    D'accord mais comment faire pour identifier la tableview par T?

    Il n'y a pas d'astuce particulière... Ton objet A peut avoir un IBOutlet NSTableView* maTableView. Il faut juste savoir que lorsque l'on réalise le lien dans InterfaceBuilder entre A.maTableView et l'instance de la NSTableView, il faut bien viser, pour accrocher la tableview et pas une de ses colonnes, par exemple...


    J'ai essayé de définir un outlet T, j'ai regardé s'il avait une zone "titre" dans "l'inspector" de la tableview. Je sèche.

    Je ne comprends pas bien ce que tu veux dire; cela signifie peut-être qu'on n'est pas tout à  fait sur la même longueur d'onde, et que l'on répond à  côté de ce que tu veux vraiment savoir... ou a besoin de savoir ! Que sais-tu des IBOutlets ?


    Par ailleurs, dans ton exemple, que doit on mettre à  la place du commentaire pour remplir la tableview?

    On ne "remplit" pas la tableview ! La tableview n'est qu'un élément de l'interface, il ne contiendra jamais de valeur. En revanche, si la tableview a un datasource, elle peut, elle-même, demander au datasource les valeurs qu'il faut afficher (en fonction du scrolling, etc). Pour cela, elle appelle la méthode
    tableView:objectValueForTableColumn:row:, les paramètres étant :
    tableView : la table view qui demande
    objectValueForTableColumn:l'identifiant de la colonne
    row:l'identifiant de la ligne
    et le datasource est censé renvoyer la valeur qu'il faut afficher à  la ligne row de la colonne donnée.

    Ce mécanisme, un peu bizarre, est formidable, puisqu'il sépare complètement l'interface des données. Une tableview réagit à  des événements, mais n'affiche que ce qui est nécessaire, obtenu sur demande auprès du datasource.

    +
    Chacha
  • RocouRocou Membre
    16:40 modifié #18
    dans 1213284436:

    Je ne comprends pas bien ce que tu veux dire; cela signifie peut-être qu'on n'est pas tout à  fait sur la même longueur d'onde, et que l'on répond à  côté de ce que tu veux vraiment savoir... ou a besoin de savoir ! Que sais-tu des IBOutlets ?

    A vrai dire comme cela ne fonctionne pas, je me suis demandé si une tableview était gérée d'une façon particulière. J'ai pourtant l'impression d'avoir fait ce qu'il fallait: définir un outlet, faire le lien entre mon controleur et ma tableview dans IB.

    J'ai ajouté le code (un copier-coller de l'exemple donné par Philippe49 dans un autre fil) dans l'implementation de mon controleur "collecteControl" et ça ne fonctionne toujours pas (ça se compile sans warning ni erreur mais rien ne s'affiche dans ma tableview)
  • RocouRocou Membre
    16:40 modifié #19
    dans 1213284436:

    On ne "remplit" pas la tableview ! La tableview n'est qu'un élément de l'interface, il ne contiendra jamais de valeur. En revanche, si la tableview a un datasource, elle peut, elle-même, demander au datasource les valeurs qu'il faut afficher (en fonction du scrolling, etc). Pour cela, elle appelle la méthode
    tableView:objectValueForTableColumn:row:, les paramètres étant :
    tableView : la table view qui demande
    objectValueForTableColumn:l'identifiant de la colonne
    row:l'identifiant de la ligne
    et le datasource est censé renvoyer la valeur qu'il faut afficher à  la ligne row de la colonne donnée.

    C'est de moins en moins clair  :'(
    Comment faire pour que la tableview demande au datasource les valeurs qu'il faut afficher?
    Tu écris, elle "appelle la méthode" mais où faut-il insérer ce code d'appel?
    Concernant les paramètres, à  la place de tableView, je mets mon outlet?
  • AliGatorAliGator Membre, Modérateur
    16:40 modifié #20
    dans 1213285463:

    dans 1213284436:

    Je ne comprends pas bien ce que tu veux dire; cela signifie peut-être qu'on n'est pas tout à  fait sur la même longueur d'onde, et que l'on répond à  côté de ce que tu veux vraiment savoir... ou a besoin de savoir ! Que sais-tu des IBOutlets ?

    A vrai dire comme cela ne fonctionne pas, je me suis demandé si une tableview était gérée d'une façon particulière. J'ai pourtant l'impression d'avoir fait ce qu'il fallait: définir un outlet, faire le lien entre mon controleur et ma tableview dans IB.

    J'ai ajouté le code (un copier-coller de l'exemple donné par Philippe49 dans un autre fil) dans l'implementation de mon controleur "collecteControl" et ça ne fonctionne toujours pas (ça se compile sans warning ni erreur mais rien ne s'affiche dans ma tableview)
    Attention, à  la base il n'est pas forcément utile de créer un Outlet dans ton Controller et de le relier à  la TableView. On le fait parfois, mais pour identifier la TableView dont on parle ([tt]if (aTableView == monOutlet)[/tt]), quoique je préfère la méthode des tags, perso, mais si tu n'as qu'une TableView l'Outlet ne servira à  rien.

    Par contre il y a une connexion à  faire absolument dans IB, c'est celle du dataSouce. C'est à  dire cliquer sur la TableView, et connecter son Outlet "dataSource" (qui est déjà  présent dans tous les objets TableView, ce n'est pas à  toi de le créer tu as juste à  le relier) à  ton cube bleu "MyController".

    Ensuite voilà  comment ça se passe : à  chaque fois qu'une TableView a besoin d'une donnée dans sa table (par exemple la colonne 5 de la ligne 3), elle demande (toute seule) à  l'objet qui est défini comme son DataSource (donc en l'occurence à  ton MyController) "tiens bonjour je suis la TableView machin et j'aimerais savoir quelle valeur je dois mettre pour la colonne C ligne L" : donc elle appelle la méthode [tt]tableView:objectValueForTableColumn:row:[/tt] sur son dataSource, et le dataSource qui implémente cette méthode retourne la bonne valeur à  la tableView.

    Donc ce n'est pas ton MyController qui a connaissance de ta TableView, mais bien l'inverse, la TableView à  qui tu as dit (via la connexion de son dataSource) à  qui il fallait qu'elle demande pour obtenir ses diverses valeurs. C'est pour ça qu'il n'est pas nécessaire d'avoir un Outlet vers ta TableView dans ton MyController.
  • RocouRocou Membre
    16:40 modifié #21
    dans 1213286467:

    Attention, à  la base il n'est pas forcément utile de créer un Outlet dans ton Controller et de le relier à  la TableView. On le fait parfois, mais pour identifier la TableView dont on parle ([tt]if (aTableView == monOutlet)[/tt]), quoique je préfère la méthode des tags, perso, mais si tu n'as qu'une TableView l'Outlet ne servira à  rien.

    Par contre il y a une connexion à  faire absolument dans IB, c'est celle du dataSouce. C'est à  dire cliquer sur la TableView, et connecter son Outlet "dataSource" (qui est déjà  présent dans tous les objets TableView, ce n'est pas à  toi de le créer tu as juste à  le relier) à  ton cube bleu "MyController".

    Ensuite voilà  comment ça se passe : à  chaque fois qu'une TableView a besoin d'une donnée dans sa table (par exemple la colonne 5 de la ligne 3), elle demande (toute seule) à  l'objet qui est défini comme son DataSource (donc en l'occurence à  ton MyController) "tiens bonjour je suis la TableView machin et j'aimerais savoir quelle valeur je dois mettre pour la colonne C ligne L" : donc elle appelle la méthode [tt]tableView:objectValueForTableColumn:row:[/tt] sur son dataSource, et le dataSource qui implémente cette méthode retourne la bonne valeur à  la tableView.

    Donc ce n'est pas ton MyController qui a connaissance de ta TableView, mais bien l'inverse, la TableView à  qui tu as dit (via la connexion de son dataSource) à  qui il fallait qu'elle demande pour obtenir ses diverses valeurs. C'est pour ça qu'il n'est pas nécessaire d'avoir un Outlet vers ta TableView dans ton MyController.
    [/quote]
    Bon si j'ai bien compris, il suffit que j'alimente le datasource pour la tableview se modifie toute seule.
    (Cela veut-il dire que les méthodes contenuent dans mon controleur sont exécutées en boucle?)

    J'ai cela comme code dans mon controleur:
    -(void) awakeFromNib<br />{<br />	NSMutableDictionary * model1=[NSMutableDictionary dictionaryWithObjectsAndKeys:@&quot;Toto&quot;,@&quot;first&quot;,@&quot;Titeuf&quot;,@&quot;second&quot;,nil];<br />	NSMutableDictionary * model2=[NSMutableDictionary dictionaryWithObjectsAndKeys:@&quot;Mommo&quot;,@&quot;first&quot;,@&quot;Mimi&quot;,@&quot;second&quot;,nil];<br />	NSMutableDictionary * model3=[NSMutableDictionary dictionaryWithObjectsAndKeys:@&quot;Rostro&quot;,@&quot;first&quot;,@&quot;Riri&quot;,@&quot;second&quot;,nil];	<br />	model=[[NSArray alloc] initWithObjects:model1,model2,model3,nil];<br />	<br />	<br />}<br /><br /> - (id)tableView:(NSTableView *)aTableView<br />&nbsp; &nbsp; objectValueForTableColumn:(NSTableColumn *)aTableColumn<br />&nbsp; &nbsp; row:(int)rowIndex<br />{<br />&nbsp;  return [[model objectAtIndex:rowIndex] objectForKey:[aTableColumn identifier]];<br />}<br /><br />- (void)tableView:(NSTableView *)aTableView<br />&nbsp; &nbsp; setObjectValue:anObject<br />&nbsp; &nbsp; forTableColumn:(NSTableColumn *)aTableColumn<br />&nbsp; &nbsp; row:(int)rowIndex<br />{<br />&nbsp; &nbsp; [[model objectAtIndex:rowIndex] setObject:anObject forKey:[aTableColumn identifier]];<br />}<br /><br />- (int)numberOfRowsInTableView:(NSTableView *)aTableView<br />{<br />&nbsp; &nbsp; return [model count];<br />}<br />
    


    Mon datasource est bien connecté à  mon controleur. Où est donc mon erreur?
  • AliGatorAliGator Membre, Modérateur
    juin 2008 modifié #22
    dans 1213289164:
    Bon si j'ai bien compris, il suffit que j'alimente le datasource pour la tableview se modifie toute seule.
    (Cela veut-il dire que les méthodes contenuent dans mon controleur sont exécutées en boucle?)
    Oui et non. C'est bien comme cela que ça fonctionne, et les méthodes de ton contrôlleur vont être appelées autant de fois que tu auras de cellules dans ton TableView, dans ton cas 6 fois (2 colonnes, 3 lignes). Mais ensuite la TableView gère tout tout seul (fais confiance à  Apple et Cocoa pour ça, ils ont un peu optimisé la chose de ce côté quand même :D). Donc ça ne va être appelé que lorsque cela s'avère nécessaire, en particulier lors du chargement de ta TableView la première fois, une fois pour le nombre de lignes puis 6 fois pour les 6 cellules, mais ensuite ça s'arrête là , ça ne va pas "boucler".

    D'ailleurs, si tu modifies tes données de ton modèle, il sera sans doute nécessaire d'en informer ta TableView pour lui indiquer de refaire appel aux méthodes de son dataSource et mettre à  jour ses données (c'est avec la méthode [tt]reloadData[/tt] de NSTableView que l'on fait cela). Et là  en effet tu auras alors sans doute besoin, quand tu arriveras à  ce cas de figure, d'un Outlet vers ta TableView pour lui envoyer ce message [tt]reloadData[/tt]... mais bon, commence sans pour ne pas te perturber et te concentrer sur le pb qui nous occupe.


    dans 1213289164:
    Mon datasource est bien connecté à  mon controleur. Où est donc mon erreur?
    En effet comme cela ça devrait marcher... A condition que tu aies bien mis aussi respectivement "first" et "second" comme [tt]identifier[/tt] de tes deux TableColumns de ta TableView dans IB pour les identifier.

    Tu peux peut-être rajouter des NSLog dans ton code, pour au moins vérifier que les méthodes de ton dataSource sont bien appelées ? et en profiter pour afficher à  ce moment les valeurs intermédiaires ([tt][model objectAtIndex:rowIndex][/tt] et/ou [tt][aTableColumn identifier][/tt] par exemple).
  • Philippe49Philippe49 Membre
    16:40 modifié #23
    dans 1213289164:


    Mon datasource est bien connecté à  mon controleur. Où est donc mon erreur?


    - (id)tableView:(NSTableView *)aTableView
        objectValueForTableColumn:(NSTableColumn *)aTableColumn
        row:(int)rowIndex
    {
      return model objectAtIndex:rowIndex] objectForKey:[b][aTableColumn identifier][/b;
    }

    - (void)tableView:(NSTableView *)aTableView
        setObjectValue:anObject
        forTableColumn:(NSTableColumn *)aTableColumn
        row:(int)rowIndex
    {
        model objectAtIndex:rowIndex] setObject:anObject forKey:[b][aTableColumn identifier][/b;
    }

    Il faut que dans IB les tableColumn possède un identifier et que cela corresponde à  un champ du modèle. Dans le code que tu m'as envoyé aucun identifier n'est défini pour les tableColumns.
    Autrement dit, si les clés sont "first" et "second" dans le model (dans awakeFromNib), les identifier définis pour la table view doivent être identiques


  • RocouRocou Membre
    16:40 modifié #24
    dans 1213295084:

    dans 1213289164:


    Mon datasource est bien connecté à  mon controleur. Où est donc mon erreur?



    Il faut que dans IB les tableColumn possède un identifier et que cela corresponde à  un champ du modèle. Dans le code que tu m'as envoyé aucun identifier n'est défini pour les tableColumns.
    Autrement dit, si les clés sont "first" et "second" dans le model (dans awakeFromNib), les identifier définis pour la table view doivent être identiques


    Ha! Enfin ça fonctionne. Merci!
    Cela dit vers où s'orienter quand le nombre de colonnes (donc leur identifier) n'est pas connu? Peut-on allouer un identifier dynamiquement?
  • Philippe49Philippe49 Membre
    juin 2008 modifié #25
    dans 1213298157:

    Cela dit vers où s'orienter quand le nombre de colonnes (donc leur identifier) n'est pas connu? Peut-on allouer un identifier dynamiquement?

    Il y a des méthodes dans NSTableColumn et NSTableView pour cela

    NSTableColumn * newColumn=[[NSTableColumn alloc] initWithIdentifier:@third];
    [tableView addTableColumn:newColumn];
    [newColumn release];
  • RocouRocou Membre
    16:40 modifié #26
    dans 1213304833:

    dans 1213298157:

    Cela dit vers où s'orienter quand le nombre de colonnes (donc leur identifier) n'est pas connu? Peut-on allouer un identifier dynamiquement?

    Il y a des méthodes dans NSTableColumn et NSTableView pour cela

    NSTableColumn * newColumn=[[NSTableColumn alloc] initWithIdentifier:@third];
    [tableView addTableColumn:newColumn];
    [newColumn release];


    Ok. Merci à  tous. Je vais bosser sur ces nouvelles acquisitions avant de revenir vous harceler  ;)
  • RocouRocou Membre
    16:40 modifié #27
    Bon, je n'arrive pas à  alimenter une tableview de façon "dynamique" (je lis un fichier, récupère une ligne que je voudrais voir comme ligne de la tableview)

    Pour dire à  la tableview d'aller chercher les données à  afficher, je mets le code suivant dans mon "controlleur":

    tableView:objectValueForTableColumn:row:

    Mais comment alimenter le datasource?
  • Philippe49Philippe49 Membre
    16:40 modifié #28
    Je crois qu'une lecture de la doc umentation s'impose.
    Des exemples y sont donnés.
    Le plus simple c'est de faire un NSArray de NSMutableDictionary ou d'une classe perso. Chaque élément de la table view correspond à  un indice dans le tableau. En l'occurrence, chaque ligne de ton fichier doit correspondre soit à  un NSMutableDictionary, ou à  une instance de ta classe perso.
  • RocouRocou Membre
    16:40 modifié #29
    dans 1216990627:

    Je crois qu'une lecture de la doc umentation s'impose.
    Des exemples y sont donnés.
    Le plus simple c'est de faire un NSArray de NSMutableDictionary ou d'une classe perso. Chaque élément de la table view correspond à  un indice dans le tableau. En l'occurrence, chaque ligne de ton fichier doit correspondre soit à  un NSMutableDictionary, ou à  une instance de ta classe perso.

    Dans ma boucle (celle qui ligne les lignes de mon fichier une par une), j'ai mis ce code:

    NSMutableDictionary * maligne=[NSMutableDictionary dictionaryWithObjectsAndKeys:valeur,@first,nil];
    model=[[NSArray alloc] initWithObjects:maligne,nil];
     
    tableView:objectValueForTableColumn:row:


    Mais rien ne s'affiche dans ma tableview et j'obtiens l'erreur suivante à  l'exécution:
    *** -[NSCFArray objectAtIndex:]: index (1) beyond bounds (1)


  • Philippe49Philippe49 Membre
    juillet 2008 modifié #30
    Peut-être :

    - (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView
    {
        return [model count];
    }
  • RocouRocou Membre
    16:40 modifié #31
    dans 1216992223:


    Peut-être :

    - (int)numberOfRowsInTableView:(NSTableView *)aTableView
    {
        return [model count];
    }

    Cette fonction n'est-elle pas appelée automatiquement?
Connectez-vous ou Inscrivez-vous pour répondre.