Glisser/déplacer des lignes contenant des objets dans une tabview

bnkbnk Membre
20:06 modifié dans API UIKit #1
Bonjour,

Je parse un fichier XML, les résultats sont stockés dans un NSMutableArray.
J'affiche ces résultats dans une UITableView, ceci fonctionne. 
Je souhaite ensuite glisser/déplacer les cellules de mon UITableView pour les réorganiser (ceci fonctionnait quand je manipulais que tu texte mais ça plante quand je manipule des objets).

Zoom sur la méthode (qui ne retourne pourtant pas d'erreurs):

<br />- (void) tableView: (UITableView *) tableView moveRowAtIndexPath: (NSIndexPath *) oldPath toIndexPath: (NSIndexPath *) newPath{<br />	NSObject *element = [list objectAtIndex:[oldPath row]];<br />	[list removeObjectAtIndex:[oldPath row]];<br />	[list insertObject:element atIndex:[newPath row]];<br />	[element release];<br />}<br />


Ceci doit surement venir du warning que j'ai au dessus lorsque je copie le NSMutableArray issue du parsing dans un NSMutableArray situé dans mon UITableViewController.

<br />//------&gt; WARNING : passing argument 1 of &#39;setList:&#39; from distinct Objective-C type<br /><br />	 // Enregistrement des résultats<br />	 self.list = mylistParser.items;<br />	 <br />//&lt;-----<br />



Voici le code complet des classes XMLToObjectParser et tabViewController:
http://paste.lisp.org/display/78623
http://paste.lisp.org/display/78622

http://paste.lisp.org/display/78624
http://paste.lisp.org/display/78625

Merci d'avance pour votre aide.

Réponses

  • allianallian Membre
    20:06 modifié #2
    pour ton warning essaye d'utiliser  une boucle pour ajouter tout les éléments de ton premier tableau à  ton deuxieme

    [list addObject:[items copy]];
    


    apres je remarque un truc il te parle de la méthode setList, elle est ou cette méthode ? c'est celle qui contient ton enregistrement de résultats ?
  • AliGatorAliGator Membre, Modérateur
    20:06 modifié #3
    La méthode setList est une méthode implicitement déclarée par la déclaration de "@property(...) ... list;"... Mais pour t'aider sur le warning, faudrait qu'on sache quels sont les types de tes objets... T'es sûr que les 2 sont mutable ?

    Sinon pour ton problème ça vient clairement de ta gestion de la mémoire :
    1) Tu demandes "objectAtIndex:oldPath.row" ce qui va te retourner un element déjà  existant.
    Donc déjà  après tu fais des manipulations dessus, mais il ne faut pas faire ensuite en dernière ligne un "release" vu que tu n'as pas fait de retain dessus avant, tu n'as aucune raison de faire ce release en l'état
    2) Mais en fait justement ton élément tu l'enlèves de ton tableau avant de le réinsérer... ce qui potentiellement fait qu'il devient pendant un temps orphelin, plus personne ne le retient (le retainCount passe à  zéro) et du coup il est potentiellement détruit... avant que tu n'aies eu le temps de le réinsérer.

    La solution c'est soit de faire l'insertion avant le remove (et dans ce cas jarter le "release" à  la fin du code qui n'a rien à  faire là ), soit tu peux faire le remove avant le insert, mais dans ce cas, il faut que tu retiennes ton objet "element" entre temps ! donc que tu lui envoies un retain avant le remove+insert, et là  dans ce cas il faut bien lui envoyer un "release" ensuite  après ces 2 lignes, pour balancer le retain que tu as fait.


    Mais là  tu as fait un "release" alors qu'il n'y a ni "alloc/init...", ni "...copy" ni "retain" de l'autre côté !! (La règle est simple pourtant...)
  • bnkbnk Membre
    20:06 modifié #4
    Salut allian,

    J'ai tenté de remplir mon premier tableau avec une boucle sans succès.
    -> pas de warning mais pas de remplissage non plus

    for(int i = 0; i &lt; [[mylistParser items] count]; i++){<br />		 [self.liste addObject:[mylistParser.items objectAtIndex:i]];<br />	 }<br />
    


    Concernant le setListe je pense qu'il s'agit tout simplement du setter pour "liste" qui doit être généré automatiquement par mon @synthesize liste.

    Sinon concernant mon plantage quand je veux drag&drop mes cellules, je pense qu'il s'agit d'une problème de gestion de mémoire.
    C'est pourquoi j'ai passé mon tableau liste en retain à  la place du copy comme ceci:
    @property (nonatomic, retain, readwrite) NSMutableArray *liste;
    


    Mais du coup j'ai un message:
    objc[69368]: FREED(id): message retain sent to freed object=0x53ee40
    
  • bnkbnk Membre
    avril 2009 modifié #5
    Très bien, j'ai pu écrire et tester ça avec les deux méthodes
    <br />	NSObject *element = [[liste objectAtIndex:[oldPath row]] retain];<br />	[liste removeObjectAtIndex:[oldPath row]];<br />	[liste insertObject:element atIndex:[newPath row]];<br />	[element release];<br />}
    


    Merci Aligator, désolé pour cette question triviale qui aurait pu être résolue en lisant la doc en profondeur mais pour ma défense c'est jamais évident de savoir par ou commencer et comme j'ai obligation de résultats je dois foncer un peu tête baissée.

    -> pour mon revenir à  ce warning, ce sont bien tout les deux des NSMutableArray.
    Tout fonctionne mais bon il y a du jaune dans le code donc si vous avez une idée je suis preneur.

    edit: après avoir intégré ce petit projet à  mon projet global le warning n'apparait plus...
  • allianallian Membre
    20:06 modifié #6
    ok ok tant mieux que ça marche, désolé pour ma mauvaise piste mais moi aussi je débute et comme toi j'ai obligation de résultat du coup des fois on fonce un peu
  • bnkbnk Membre
    avril 2009 modifié #7
    Pas de soucis allian, c'est sympa de vouloir aider. Puis c'est en se trompant qu'on apprend je pense.
    Bon courage pour ton projet.
  • bnkbnk Membre
    20:06 modifié #8
    Je reviens vers vous pour un problème qui concerne toujours cette foutues tableView:

    * Comment récupérer directement l'objet contenu dans une cellule ou une ligne à  partir d'une méthode isolée? (j'ai vu qu'une demande similaire avait été déposée sur ce forum mais la méthode utilisée n'est pas dispo sur iPhone :@)


    * J'ai un problème, lors de la création de mes cellules. Si je scroll vers le bas de mon tableau, le texte de ma cellule 1 va se superposer au texte de ma cellule 10. Si je remonte vers ma cellule 1, je constate que le texte de ma cellule 10 vient de se superposer au texte de ma cellule 1.
    -> ce phénomène n'apparait pas lorsque j'ajoute mon texte avec "cell.text" mais je souhaite utiliser une UIView pour pouvoir customiser d'avantage l'apparence de mon texte (retour à  la ligne).

    Voici mon code:

    <br />// Customize the appearance of table view cells.<br />- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {<br />	static NSString *CellIdentifier = @&quot;Cell&quot;;<br />	<br />		//on réutilise une cell déjà  créee<br />	UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];<br />	<br />	if(cell == nil) <br />	{<br />		//creation de la cell<br />		cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];<br />	}<br />	<br />	//cell.text = [[self.liste objectAtIndex:indexPath.row] libele];<br />	//cell.font = [UIFont fontWithName:@&quot;Helvetica&quot; size:12];<br />	//cell.image = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@&quot;imageCell&quot; ofType:@&quot;png&quot;]];<br />	<br />	cell.clearsContextBeforeDrawing;<br />	<br />	//création image de fond<br />	UIImage *imageCell = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@&quot;fond_cellule&quot; ofType:@&quot;png&quot;]];<br />	<br />	//on crée une imageView contenant l&#39;image de fond et on l&#39;ajoute comme backgroundView de la Cell<br />	UIImageView *viewImageCell = [[UIImageView alloc] initWithImage:imageCell];<br />	cell.backgroundView= viewImageCell;<br />	<br />	//création d&#39;un label<br />	CGRect contentRect = CGRectMake(10, 0.0, 280, 43);<br />	UILabel *textCell = [[UILabel alloc] initWithFrame:contentRect];<br />	<br />	//on ajoute le texte au label<br />	textCell.clearsContextBeforeDrawing;<br />	textCell.numberOfLines =&nbsp; 3;<br />	textCell.text = [[self.liste objectAtIndex:indexPath.row] libele];<br />	textCell.font = [UIFont fontWithName:@&quot;Helvetica&quot; size:12];<br />	textCell.textColor = [UIColor blackColor];<br />	textCell.backgroundColor = [UIColor clearColor];<br />	textCell.opaque = YES;<br />	<br />	//on crée une vue contenant le label contenant le texte<br />	UIView *viewCell = [[UIView alloc] initWithFrame:contentRect];<br />	viewCell.clearsContextBeforeDrawing;<br />	[viewCell addSubview:textCell];<br />	[textCell release];<br />	<br />	//on ajoute la vue comme contentView de la cell<br />	[cell.contentView addSubview:viewCell];<br />	[viewCell release];<br /><br />	return cell;<br />}
    


    merci
  • AliGatorAliGator Membre, Modérateur
    20:06 modifié #9
    dans 1239961305:

    * Comment récupérer directement l'objet contenu dans une cellule ou une ligne à  partir d'une méthode isolée? (j'ai vu qu'une demande similaire avait été déposée sur ce forum mais la méthode utilisée n'est pas dispo sur iPhone :@)
    L'idée n'est pas de demander à  la cellule son contenu, en soi, mais de demander au dataSource ce genre d'informations.
    Je ne sais pas ce que tu entends par "l'objet contenu dans une cellule", si tu veux récupérer la UIView représentant le contenu graphique de la cellule par exemple, ou si tu veux récupérer un objet associé à  la cellule (l'objet que la cellule représente, dont la représentation à  l'écran dans ta TableView n'est éventuellement qu'une façon comme une autre de le représenter)

    Mais dans tous les cas, c'est au dataSource qu'il faut demander ça. Soit c'est un objet perso (d'une classe perso) que tu veux récupérer, alors tu rajoutes à  ton dataSource une méthode "tableView: monObjetForRowAtIndexPath:" sur le même modèle que les autres méthodes de dataSource, et tu l'appelles quand tu as besoin...
    Soit si c'est la vue contenue dans ta cell, autrement dit la "contentView" de ton UITableViewCell, il suffit de demander [tt][dataSource tableView:tv cellForRowAtIndexPath:[tv indexPathForSelectedRow]];[/tt]. (A la limite tu peux ne pas le demander explicitement à  ton dataSource mais à  ta TableView, via [tv cellForRowAtIndexPath:[tv indexPathForSelectedRow]], mais au final cela va avoir pour effet sous le capot d'appeler le dataSource alors bon)
  • bnkbnk Membre
    avril 2009 modifié #10
    Merci pour tes explications je comprend mieux le rôle du data source.

    * Tu remarqueras dans mon code que je n'utilise aucun dataSource, je ne fait qu'attribuer des labels à  mes cellules en parcourant un tableau (c'est donc un dataSource s'en en être réellement un..).
    Il faudrait que je fasse un truc du genre (c'est faux):
    self.tableView.dataSource = self.liste
    



    * J'avais pris connaissance dans la doc des méthodes:

    - (NSIndexPath *)indexPathForCell:(UITableViewCell *)cell {<br /><br />- (UITableViewCell *)cellForRowAtIndexPath:(NSIndexPath *)indexPath<br />
    


    Ma question se précise: comment récupérer un indexPath sans toucher ni sélectionner une cellule ?
    Je souhaite en fait qu'à  chaque fois que j'utilise ma méthode :
    - (void) tableView: (UITableView *) tableView moveRowAtIndexPath: (NSIndexPath *) oldPath toIndexPath: (NSIndexPath *) newPath
    

    il faudrait qu'une autre méthode soit appelée pour rafraichir l'ensemble de mon tableView (que les cell se recréent) suite aux modifications apportées par ma méthode moveRowAtIndex.

    ex: Dans le cas de mon exercice ou il faut déplacer les row au bon endroit, je veux que l'image de fond soit modifiée si ma row est bien positionnée.

    nb: mon dataSource devra donc être ce fameux tableau "liste" qui est en fait un objet d'éléments contenant un label, un ordre.
    -> j'affiche le libellé dans ma cellule, l'ordre sert à  contrôler si il est bien placé à  la bonne row (dans ce cas j'affiche une image de fond particulière).

    code à  jour:
    <br />#import &quot;tabViewController.h&quot;<br />#import &quot;elementList.h&quot;<br />#import &quot;XMLToObjectParser.h&quot;<br /><br />@implementation tabViewController<br />@synthesize liste, viewImageCell, cell, imageCellBad, imageCellOK;<br /><br /> - (void)viewDidLoad {<br /> [super viewDidLoad];<br />		//Presentation tableView<br />	 [self.tableView setSeparatorColor:[UIColor redColor]];<br />	 //[self.tableView setBackgroundColor:[UIColor lightGrayColor]];<br />	 self.title = @&quot;Etapes de la Création d&#39;Entreprise&quot;;<br />	 [self.tableView setEditing:YES animated:YES];<br /><br />	 //-------------------------------------------------&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;<br />	 self.tableView.dataSource = self;<br />	 <br />	 //Adresse du XML<br />	 NSString *monURL = [[NSBundle mainBundle] pathForResource:@&quot;draglist&quot; ofType:@&quot;xml&quot;];<br />	 NSURL *url = [NSURL fileURLWithPath:monURL];<br />	 <br />	 //Activation du Parser<br />	 XMLToObjectParser *mylistParser = [[[XMLToObjectParser alloc] init] parseXMLAtURL:url toObject:@&quot;elementList&quot; parseError:nil];<br />	 <br />	 // Enregistrement des résultats<br />	 self.liste = mylistParser.items;<br />	 <br />	 //Test résultat en console<br />	 for(int i = 0; i &lt; [[mylistParser items] count]; i++){<br />		 NSLog(@&quot;ordre element %@&quot;, [[[mylistParser items] objectAtIndex:i] ordre]);<br />		 NSLog(@&quot;libele %@&quot;, [[[mylistParser items] objectAtIndex:i] libele]);<br />		 NSLog(@&quot;ordre element (list)%@&quot;, [[self.liste objectAtIndex:i] ordre]);<br />		 NSLog(@&quot;ordre element (list)%@&quot;, [[self.liste objectAtIndex:i] libele]);<br /> }<br />	 [mylistParser release];<br /> } <br /><br /><br />#pragma mark Table view methods<br /><br />// Customize the number of rows in the table view.<br />- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {<br />&nbsp; &nbsp; return [self.liste count];<br />}<br /><br /><br />// Customize the appearance of table view cells.<br />- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {<br />	static NSString *CellIdentifier = @&quot;Cell&quot;;<br />	<br />		//on réutilise une cell déjà  créee<br />	cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];<br />	<br />	if(cell == nil) <br />	{<br />		//creation de la cell<br />		cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];<br />	}<br />	<br />	//cell.text = [[self.liste objectAtIndex:indexPath.row] libele];<br />	//cell.font = [UIFont fontWithName:@&quot;Helvetica&quot; size:12];<br />	//cell.image = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@&quot;imageCell&quot; ofType:@&quot;png&quot;]];<br />	<br />	cell.clearsContextBeforeDrawing;<br />	<br />	//création image de fond<br />	UIImage *imageCell = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@&quot;fond_cellule&quot; ofType:@&quot;png&quot;]];<br />	imageCellOK = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@&quot;fond_cellule_green&quot; ofType:@&quot;png&quot;]];<br />	imageCellBad = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@&quot;fond_cellule_red&quot; ofType:@&quot;png&quot;]];<br />	<br />	/*<br />	//on crée une imageView contenant l&#39;image de fond et on l&#39;ajoute comme backgroundView de la Cell<br />	UIImageView *viewImageCell = [[UIImageView alloc] initWithImage:imageCell];<br />	cell.backgroundView= viewImageCell;<br />	*/<br /><br />	NSString* position = [NSString stringWithFormat:@&quot;%d&quot;, indexPath.row+1];<br />	NSString *resultat = [[liste objectAtIndex:indexPath.row] ordre];<br />	viewImageCell = [[UIImageView alloc] initWithImage:imageCell];<br />	if([position isEqual:resultat])	viewImageCell = [[UIImageView alloc] initWithImage:imageCellOK];<br />	else viewImageCell = [[UIImageView alloc] initWithImage:imageCellBad];<br />	cell.backgroundView= viewImageCell;<br />	<br />	//création d&#39;un label<br />	CGRect contentRect = CGRectMake(14, 0.0, 260, 43); //0, 0, 280, 43<br />	UILabel *textCell = [[UILabel alloc] initWithFrame:contentRect];<br />	<br />	//on ajoute le texte au label<br />	textCell.clearsContextBeforeDrawing;<br />	textCell.numberOfLines =&nbsp; 3;<br />	textCell.text = [[self.liste objectAtIndex:indexPath.row] libele];<br />	textCell.font = [UIFont fontWithName:@&quot;Helvetica&quot; size:12];<br />	textCell.textColor = [UIColor blackColor];<br />	textCell.backgroundColor = [UIColor clearColor];<br />	textCell.opaque = YES;<br />	<br />	//on crée une vue contenant le label contenant le texte<br />	UIView *viewCell = [[UIView alloc] initWithFrame:contentRect];<br />	viewCell.clearsContextBeforeDrawing;<br />	[viewCell addSubview:textCell];<br />	[textCell release];<br />	<br />	//on ajoute la vue comme contentView de la cell<br />	[cell.contentView addSubview:viewCell];<br />	[viewCell release];<br />	<br />	//NSLog(@&quot;intitules ----&gt; %@&quot;, [[self.liste objectAtIndex:indexPath.row] libele]);<br />	return cell;<br />}<br /><br /><br /><br />// Modification du style EDIT en retirant la possibilité de supprimer la row (juste les réorganiser)<br />- (UITableViewCellEditingStyle) tableView:(UITableView *) tableView editingStyleForRowAtIndexPath:(NSIndexPath *) indexPath {<br />	return UITableViewCellEditingStyleNone;<br />}<br /><br /><br />- (void) tableView: (UITableView *) tableView moveRowAtIndexPath: (NSIndexPath *) oldPath toIndexPath: (NSIndexPath *) newPath{<br />	<br />	NSObject *element = [[liste objectAtIndex:[oldPath row]] retain];<br />	[liste removeObjectAtIndex:[oldPath row]];<br />	[liste insertObject:element atIndex:[newPath row]];<br />	<br />		//test si il est bien placé<br />	<br />	NSLog(@&quot;NSIndexPath: %d&#092;n&quot;, oldPath);<br />	NSLog(@&quot;NSIndexPath: %d&#092;n&quot;, newPath);<br />	NSLog(@&quot;old path: %d&#092;n&quot;, [oldPath row] +1);<br />	NSLog(@&quot;new path: %d&#092;n&quot;, [newPath row] +1);<br />	NSLog(@&quot;perfect path: %@&#092;n&quot;, [[liste objectAtIndex:[oldPath row]] ordre]);<br />	<br />	NSString* position = [NSString stringWithFormat:@&quot;%d&quot;, [newPath row]+1];<br />	NSString *resultat = [[liste objectAtIndex:[oldPath row]] ordre];<br />	//if([position isEqual:resultat])	viewImageCell = [[UIImageView alloc] initWithImage:imageCellOK];<br />	//else viewImageCell = [[UIImageView alloc] initWithImage:imageCellBad];<br /><br />}<br /><br /><br /><br />/*<br />- (NSIndexPath *)indexPathForCell:(UITableViewCell *)cell {<br />	<br />}<br />- (UITableViewCell *)cellForRowAtIndexPath:(NSIndexPath *)indexPath {<br />}<br />*/<br /><br />- (void)didReceiveMemoryWarning {<br />&nbsp; &nbsp; [super didReceiveMemoryWarning]; // Releases the view if it doesn&#39;t have a superview<br />&nbsp; &nbsp; // Release anything that&#39;s not essential, such as cached data<br />}<br /><br />- (void)dealloc {<br />&nbsp; &nbsp; [super dealloc];<br />}<br /><br /><br />@end<br />
    
  • AliGatorAliGator Membre, Modérateur
    20:06 modifié #11
    dans 1239971533:

    * Tu remarqueras dans mon code que je n'utilise aucun dataSource, je ne fait qu'attribuer des labels à  mes cellules en parcourant un tableau (c'est donc un dataSource s'en en être réellement un..).
    Bah heu... c'est justement ça un dataSource, c'est un objet qui se charge de retourner la bonne cellule pour l'indexPath demandé... D'ailleurs si tu utilises précisément un dataSource, puisque la méthode dont tu as mis le code dans ton post... c'est justement implémentation du @protocol UITableViewDataSource !!
    Donc l'objet qui implémente cette méthode (l'objet dont tu as extrait ce code que tu nous a mis sur le forum), bah c'est lui ton dataSource !!
    Qui puise ses données depuis le tableau "liste" dans ton cas... ce qui est en fait un grand classique (que le dataSource d'une TableView puise ses infos dans un NSArray)

    ... Donc si justement tu as un dataSource (et c'est une bonne chose) :P

    dans 1239971533:

    * J'avais pris connaissance dans la doc des méthodes:

    - (NSIndexPath *)indexPathForCell:(UITableViewCell *)cell {<br /><br />- (UITableViewCell *)cellForRowAtIndexPath:(NSIndexPath *)indexPath<br />
    


    Ma question se précise: comment récupérer un indexPath sans toucher ni sélectionner une cellule ?
    Je souhaite en fait qu'à  chaque fois que j'utilise ma méthode :
    - (void) tableView: (UITableView *) tableView moveRowAtIndexPath: (NSIndexPath *) oldPath toIndexPath: (NSIndexPath *) newPath
    

    il faudrait qu'une autre méthode soit appelée pour rafraichir l'ensemble de mon tableView (que les cell se recréent) suite aux modifications apportées par ma méthode moveRowAtIndex.
    Je suis pas sûr d'avoir bien compris ta demande, mais si c'est le cas, je pense que "reloadData" est fait pour ça... Puisqu'il va demander au dataSource de refournir les bonnes infos.

    Sinon un appel à  "setNeedsDisplay" sur ta tableView peut amplement suffire, plutôt que de demander à  la TableView de recharger toutes ses données mais seulement de redessiner les cellules visibles.



    Sinon petite remarque, le principe des "reuseIdentifier" n'est pas pour rien : l'idée est en fait sous le capot de ne créer qu'une celle UITableViewCell réutilisable, et à  chaque fois que ta TableView te demande une UITableViewCell, tu récupères celle déjà  pré-créée (avec le reuseIdentifier), tu l'adaptes (modifies son texte pour correspondre selon l'indexPath demandé), et la renvoie... plutôt que de créer autant de UITableViewCells que tu as réellement de cellule.

    Du coup, l'idée c'est de regrouper toutes les propriétés qui sont communes à  toutes tes cellules au moment où tu crées la cellule : si ta cellule n'existe pas encore (donc dans le [tt]if (cell == nil) ...[/tt]), tu fais le alloc/init pour créer la cellule, puis tu la préèconfigures avec toutes les propriétés communes à  toutes tes cellules. Et si la cellule existe déjà , (donc que [tt]dequeueReusableCellWithIdentifier:[/tt] n'a pas retourné nil mais bien la cellule pré-configurée créée lors d'un précédent passage dans ta méthode), bah tu te contentes ensuite d'adapter juste ce qui change d'une cellule à  l'autre.

    Donc pour ton exemple, le réglage de clearsContextBeforeDrawing, le chargement de ton UIImage, la création de ton UIImageView pour l'affecter à  la backgroundView, la création du label et sa configuration (police, couleur, ...) et tout le toutim va dans le "if (cell == nil)" !!
    Et finalement il n'y a que [tt]textCell.text = [[self.liste objectAtIndex:indexPath.row] libele];[/tt], qui lui change en fonction de l'indexPath demandé, qui doit être à  l'extérieur du if (bien sûr il faut que tu récupères ton textCell hors du if, pour les cas où tu ne passes pas dans le "if" car tu récupères la UITableViewCell déjà  créée, mais en demandant la subview x de la subview y de la contentView de ta cell, tu devrais pouvoir la récupérer pour affecter son texte.
  • bnkbnk Membre
    20:06 modifié #12
    Merci pour le temps passé à  rédiger cette explication très claire et adaptée!

    C'est pas si bête que ça finalement le faire de réutiliser une cell pré formatée. ça ne m'avais pas sauté en yeux, je ne comprennais pas pourquoi il y avait autant de paramètres supposés identifier une cellule (indexPath, cellIdentifier, row) c'est maintenant clair ;)

    Sauf que dans mon code, ca ne semble pas fonctionner!
    Il ne m'est plus possible de voir la cell #10 sans que ça plante.

    J'ai donc mis un log dans le if supposé créer qu'une et une seule fois un modèle de cell.
    Surprise j'ai 8 : "CELL
    > NIL" qui apparaissent au lancement de mon application là  ou je devrais pouvoir le voir apparaitre qu'une seule fois.
    Qu'est ce que j'ai mal fait?


    <br />#import &quot;tabViewController.h&quot;<br />#import &quot;elementList.h&quot;<br />#import &quot;XMLToObjectParser.h&quot;<br /><br />@implementation tabViewController<br />@synthesize liste, viewImageCell, imageCellBad, imageCellOK;<br /><br /> - (void)viewDidLoad {<br /> [super viewDidLoad];<br />		//Presentation tableView<br />	 [self.tableView setSeparatorColor:[UIColor redColor]];<br />	 //[self.tableView setBackgroundColor:[UIColor lightGrayColor]];<br />	 self.title = @&quot;Etapes de la Création d&#39;Entreprise&quot;;<br />	 [self.tableView setEditing:YES animated:YES];<br />	 <br />	 //Adresse du XML<br />	 NSString *monURL = [[NSBundle mainBundle] pathForResource:@&quot;draglist&quot; ofType:@&quot;xml&quot;];<br />	 NSURL *url = [NSURL fileURLWithPath:monURL];<br />	 <br />	 //Activation du Parser<br />	 XMLToObjectParser *mylistParser = [[[XMLToObjectParser alloc] init] parseXMLAtURL:url toObject:@&quot;elementList&quot; parseError:nil];<br />	 <br />	 // Enregistrement des résultats<br />	 self.liste = mylistParser.items;<br />	 <br />	 //Test résultat en console<br />	 for(int i = 0; i &lt; [[mylistParser items] count]; i++){<br />		 NSLog(@&quot;ordre element %@&quot;, [[[mylistParser items] objectAtIndex:i] ordre]);<br />		 NSLog(@&quot;libele %@&quot;, [[[mylistParser items] objectAtIndex:i] libele]);<br />		 NSLog(@&quot;ordre element (list)%@&quot;, [[self.liste objectAtIndex:i] ordre]);<br />		 NSLog(@&quot;ordre element (list)%@&quot;, [[self.liste objectAtIndex:i] libele]);<br /> }<br />	 [mylistParser release];<br /> } <br /><br /><br />#pragma mark Table view methods<br /><br />// Customize the number of rows in the table view.<br />- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {<br />    return [self.liste count];<br />}<br /><br /><br />// Customize the appearance of table view cells.<br />- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {<br />	static NSString *CellIdentifier = @&quot;Cell&quot;;<br />	<br />		//on réutilise une cell déjà  créee<br />	UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];<br />	UIImage *imageCell = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@&quot;fond_cellule&quot; ofType:@&quot;png&quot;]];<br />	UILabel *textCell;<br />	viewImageCell = [[UIImageView alloc] initWithImage:imageCell];<br />	<br />	if(cell == nil) <br />	{<br />		NSLog(@&quot;CELL ------&gt; NIL&quot;);<br />		//creation de la cell<br />		cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];<br />	<br />		//cell.text = [[self.liste objectAtIndex:indexPath.row] libele];<br />		//cell.font = [UIFont fontWithName:@&quot;Helvetica&quot; size:12];<br />		//cell.image = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@&quot;imageCell&quot; ofType:@&quot;png&quot;]];<br />		<br />		cell.clearsContextBeforeDrawing;<br />		<br />		//création images de fond<br />		imageCellOK = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@&quot;fond_cellule_green&quot; ofType:@&quot;png&quot;]];<br />		imageCellBad = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@&quot;fond_cellule_red&quot; ofType:@&quot;png&quot;]];<br />		<br />		<br />		//création d&#39;un label<br />		CGRect contentRect = CGRectMake(14, 0.0, 260, 43); //0, 0, 280, 43<br />		textCell = [[UILabel alloc] initWithFrame:contentRect];<br />		<br />		//on ajoute un label<br />		textCell.clearsContextBeforeDrawing;<br />		textCell.numberOfLines =  3;<br />		textCell.font = [UIFont fontWithName:@&quot;Helvetica&quot; size:12];<br />		textCell.textColor = [UIColor blackColor];<br />		textCell.backgroundColor = [UIColor clearColor];<br />		textCell.opaque = YES;<br />		<br />		//on crée une vue contenant le label contenant le texte<br />		UIView *viewCell = [[UIView alloc] initWithFrame:contentRect];<br />		viewCell.clearsContextBeforeDrawing;<br />		[viewCell addSubview:textCell];<br />		[textCell release];<br />		<br />		//on ajoute la vue comme contentView de la cell<br />		[cell.contentView addSubview:viewCell];<br />		[viewCell release];<br />		<br />	}<br />	<br />/*		//on modifie l&#39;image de fond en fonction de la situation de la cell<br />	NSString* position = [NSString stringWithFormat:@&quot;%d&quot;, indexPath.row+1];<br />	NSString *resultat = [[liste objectAtIndex:indexPath.row] ordre];<br />	viewImageCell = [[UIImageView alloc] initWithImage:imageCell];<br />	if([position isEqual:resultat])	viewImageCell = [[UIImageView alloc] initWithImage:imageCellOK];<br />	else viewImageCell = [[UIImageView alloc] initWithImage:imageCellBad];<br />	cell.backgroundView= viewImageCell;<br />*/<br />	<br />		//on modifie le text du label<br />	textCell.text = [[self.liste objectAtIndex:indexPath.row] libele];<br />	<br />	return cell;<br />}<br /><br />// Modification du style EDIT en retirant la possibilité de supprimer la row (juste les réorganiser)<br />- (UITableViewCellEditingStyle) tableView:(UITableView *) tableView editingStyleForRowAtIndexPath:(NSIndexPath *) indexPath {<br />	return UITableViewCellEditingStyleNone;<br />}<br /><br /><br />- (void) tableView: (UITableView *) tableView moveRowAtIndexPath: (NSIndexPath *) oldPath toIndexPath: (NSIndexPath *) newPath{<br />	<br />	NSObject *element = [[liste objectAtIndex:[oldPath row]] retain];<br />	[liste removeObjectAtIndex:[oldPath row]];<br />	[liste insertObject:element atIndex:[newPath row]];<br />	<br />		//test si il est bien placé<br />	<br />	NSLog(@&quot;NSIndexPath: %d&#092;n&quot;, oldPath);<br />	NSLog(@&quot;NSIndexPath: %d&#092;n&quot;, newPath);<br />	NSLog(@&quot;old path: %d&#092;n&quot;, [oldPath row] +1);<br />	NSLog(@&quot;new path: %d&#092;n&quot;, [newPath row] +1);<br />	NSLog(@&quot;perfect path: %@&#092;n&quot;, [[liste objectAtIndex:[oldPath row]] ordre]);<br /><br />	for(int i = 0; i &lt; [liste count]; i++){<br />	NSLog(@&quot;ordre: %@&#092;n&quot;, [[liste objectAtIndex:i] ordre]);<br />	}<br />	<br />	//on redessine les cells affichées<br />	[self.tableView setNeedsDisplay];<br />	//[self.tableView reloadData];<br />	<br />	//[self.tableView cellForRowAtIndexPath:newPath];<br />	/*<br />	NSString* position = [NSString stringWithFormat:@&quot;%d&quot;, [newPath row]+1];<br />	NSString *resultat = [[liste objectAtIndex:[oldPath row]] ordre];<br />	if([position isEqual:resultat])	viewImageCell = [[UIImageView alloc] initWithImage:imageCellOK];<br />	else viewImageCell = [[UIImageView alloc] initWithImage:imageCellBad];<br />	*/<br /><br />}<br /><br /><br /><br />/*<br />- (NSIndexPath *)indexPathForCell:(UITableViewCell *)cell {<br />	<br />}<br />- (UITableViewCell *)cellForRowAtIndexPath:(NSIndexPath *)indexPath {<br />}<br />*/<br /><br />- (void)didReceiveMemoryWarning {<br />    [super didReceiveMemoryWarning]; // Releases the view if it doesn&#39;t have a superview<br />    // Release anything that&#39;s not essential, such as cached data<br />}<br /><br />- (void)dealloc {<br />    [super dealloc];<br />}<br /><br /><br />@end<br />
    


    info: [self.tableView reloadData]; plante mon appli
    2009-04-17 16:10:18.042 draglist[4373:20b] *** Terminating app due to uncaught exception &#39;NSRangeException&#39;, reason: &#39;*** -[NSCFArray insertObject:atIndex:]: index (1) beyond bounds (1)&#39;<br />2009-04-17 16:10:18.042 draglist[4373:20b] Stack: (....
    


    Comment modifier ce code pour qu'il soit appliqué à  la cell située dans mon "newPath"?
    <br />	NSString* position = [NSString stringWithFormat:@&quot;%d&quot;, [newPath row]+1];<br />	NSString *resultat = [[liste objectAtIndex:[oldPath row]] ordre];<br />	if([position isEqual:resultat])	viewImageCell = [[UIImageView alloc] initWithImage:imageCellOK];<br />	else viewImageCell = [[UIImageView alloc] initWithImage:imageCellBad];<br />
    


    [move]merci[/move]
  • AliGatorAliGator Membre, Modérateur
    20:06 modifié #13
    J'ai pas eu le temps de trop lire ton code pour le crash avec OutOfBoundsException, par contre pour ce qui est des 8 "CELL ----> NIL" que tu as, j'ai réalisé récemment que c'est p'tet normal en fait : d'ailleurs si je met un NSLog dans mon "if" qui recrée une nouvelle cell, j'en ai aussi plusieurs dans mes applis.
    Du coup je pense que plutôt que de ne créer qu'une seule cell par reuseIdentifier comme je pensais qu'il faisait, il se peut qu'il crée plusieurs instances de cells (c'est ce que suggère le NSLog de toute façon)... mais pas plus que le nombre nécessaire à  l'affichage courant à  l'écran. C'est à  dire que si tu as un tableau avec 500 cellules, mais que 8 visibles à  la fois, il va instancier 8 UITableViewCells (et non pas une seule), mais va les réutiliser pendant le scroll, pour pas en créer 500, mais n'avoir à  manipuler que les 8 maximum affichées à  l'écran.

    Du coup ton cas des 8 "CELL
    > NIL" est normal, en tout cas ça semble être le comportement que tout le monde a, je pense pas que tu aies mal fait quoi que ce soit (au contraire tu as bien fait car tu m'as fait réaliser que le fonctionnement interne n'était pas celui que je pensais :D)

    Voilà  maintenant pour ton plantage je pense que c'est une mauvaise gestion du cas de reuse, vérifie dans les cas où tu ne passes pas dans ton "if" (cas où tu scroll donc typiquement d'après ce que je viens d'expliquer) que tu as bien accès à  toutes tes variables (et que tu n'utilises pas une variable qui serait initialisée dans ton if, mais du coup pas initialisée et pointant vers n'importe quoi si tu ne passes pas dans ton "if")

    Aussi, j'ai vu que tu avais une belle double fuite mémoire sur ton viewImageCell (dans "tableView:cellForRowAtIndexPath:")...
    - d'une part tu l'affectes à  une nouvelle avec alloc/init, mais dans les 2 lignes qui suivent ensuite, que tu passes dans le if ou dans le else, dans les 2 cas tu refais un alloc/init... du coup le premier que tu as fait qui "initWithImage:imageCell" non seulement ne sert à  rien, mais en plus alloue un objet sans le relâcher, donc fuite mémoire (tu crées des objets en mémoire sans jamais les relâcher, en plus autant qu'il y a de cellules dans ta table... bonjour la montée de ton empreinte mémoire dans ton app)
    - d'autre part une fois le alloc/init fait (dans le if ou dans le else de ces lignes), tu l'affectes à  cell.backgroundView. Très bien... sauf que cette propriété est de type "@property(nonatomic, retain)"... ce qui veut dire qu'elle se charge de retenir sa valeur, de devenir le "propriétaire" de cette UIImageView. Du coup toi, qui a fait le alloc/init, c'est à  toi de faire le "release" maintenant que tu as "passé la main". Faire un "alloc/init" sans faire de "release" correspondant c'est le meilleur moyen de faire une fuite mémoire, ça fait partie des règles de base de la gestion mémoire. Il faut donc qu'une fois cette viewImageCell affectée à  cell.background, tu fasses un release sur viewImageCell !

    Si ça se trouve (bien que ça m'étonnerait quand même surtout sur simulateur) c'est p'tet la raison de ton plantage, une montée en conso mémoire un peu rapide à  cause de cette double fuite qui en plus est multipliée par ton nombre de cellules de ta tableView...

    Mais la gestion de la mémoire est primordiale encore plus sur un device mobile comme l'iPhone où la mémoire est limitée, on ne peut pas faire n'importe quoi !!
  • bnkbnk Membre
    20:06 modifié #14
    Salut et merci pour tes explications.

    Je me replonge dans tout ceci en fin d'aprém.
    Il est vrai que la gestion de la mémoire est loin d'être mon fort, merci à  JAVA.
    Je vais donc essayer de corriger tout ceci et posterai un code correct ça servira certainement à  d'autre débutants.
  • bnkbnk Membre
    20:06 modifié #15
    dans 1239972475:
    il faut que tu récupères ton textCell hors du if, pour les cas où tu ne passes pas dans le "if" car tu récupères la UITableViewCell déjà  créée, mais en demandant la subview x de la subview y de la contentView de ta cell, tu devrais pouvoir la récupérer pour affecter son texte.


    Il semble que ce soit plus compliqué que prévu, je n'arrive pas vraiment à  y accèder pourtant j'ai essayer de tagger et de faire subview.text des choses comme ca..

    <br />- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {<br />	<br />	static NSString *CellIdentifier = @&quot;Cell&quot;;<br />	<br />	//on réutilise une cell déjà  créee<br />	UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];<br />	<br />	//creation des composants de la cell<br />	CGRect contentRect = CGRectMake(14, 0.0, 260, 43); //0, 0, 280, 43<br />	<br />	if(cell == nil) <br />	{<br />		NSLog(@&quot;nouvelle cell : %d&quot;, indexPath.row+1);<br />		<br />		//creation de la cell<br />		cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];<br />		<br />		//cell.text = [[self.liste objectAtIndex:indexPath.row] libele];<br />		//cell.font = [UIFont fontWithName:@&quot;Helvetica&quot; size:12];<br />		//cell.image = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@&quot;imageCell&quot; ofType:@&quot;png&quot;]];<br />		<br />		cell.clearsContextBeforeDrawing;<br />		<br />		//on ajoute un label<br />		textCell = [[UILabel alloc] initWithFrame:contentRect];<br />		textCell.clearsContextBeforeDrawing;<br />		textCell.numberOfLines =&nbsp; 3;<br />		textCell.font = [UIFont fontWithName:@&quot;Helvetica&quot; size:12];<br />		textCell.textColor = [UIColor blackColor];<br />		textCell.backgroundColor = [UIColor clearColor];<br />		textCell.opaque = YES;<br />		<br />		//textCell.text = [[self.liste objectAtIndex:indexPath.row] libele];<br />		<br />		//on ajoute la vue comme contentView de la cell<br />		[textCell setTag:1];<br />		[cell.contentView addSubview:textCell];<br />		[textCell release];<br />	}<br />	<br />	NSLog(@&quot;cell existante: %d&quot;, indexPath.row+1);<br />	NSLog(@&quot;libele: %@&quot;, [[self.liste objectAtIndex:indexPath.row] libele]);<br />	NSLog(@&quot;ma view tag %@&quot;,[cell viewWithTag:1]);<br /><br />	//--------&gt; JE DOIS CHANGER LA VALEUR DE &quot;TEXTCELL&quot; HORS DU IF ICI<br />	<br />	//cell.contentView.subviews.text&nbsp; = [[self.liste objectAtIndex:indexPath.row] libele];&nbsp; //c&#39;est pas bon<br />	//[[cell viewWithTag:1] set ... // je ne sais pas comment faire ca..<br />	<br />	//cell.text = [[self.liste objectAtIndex:indexPath.row] libele]; //si je fais ca tout fonctionne mais bon je passe pas par ma subview, ce n&#39;est pas ce que je veux<br />	<br />	return cell;<br />}<br />
    

    Sauf si tu as une idée, je crois que je vais devoir faire une subclass cell. Je ne sais pas trop comment mais bon je ne vois pas trop d'autre solutions.
  • AliGatorAliGator Membre, Modérateur
    20:06 modifié #16
    Bah tu y es presque...

    #define TEXTCELL_TAG 100<br /><br />-(UITableViewCell*)... cellForRowAtIndexPath... <br />{<br />  // 1) j&#39;ai l&#39;impression que tu as déclaré textCell en variable d&#39;instance de ta classe,<br />  // mais c&#39;est pas utile, une déclaration locale suffit, comme ici :<br /><br />  UILabel* textCell = nil;<br />  ...<br />  UITableViewCell* cell = [tableView dequeue...];<br />  if (cell == nil)<br />  {<br />      .... creation de la cell et ajout des subviews<br />      textCell = [[UILabel alloc] initWithFrame:...];<br />      textCell.tag = TEXTCELL_TAG;<br />      [cell.contentView addSubview:textCell];<br />      [textCell release];<br />      ...<br />  }<br /><br />  // ici qu&#39;on soit passé dans le if ou pas, récupérer la textCell de toute façon<br />  textCell = [cell.contentView viewWithTag:TEXTCELL_TAG];<br />  // et on peut continuer : textCell est un UILabel, tu peux accéder à  ses propriétés comme n&#39;importe quel UILabel<br />  textCell.text = [[self.liste objectAtIndex:indexPath.row] libele];<br />  ...<br />}
    
    Tu vois t'étais pas loin ;D


    PS : Y'a 2 "L" à  "Libellé" :)
  • bnkbnk Membre
    avril 2009 modifié #17
    En effet je n'étais pas loin grrr! merci ;)

    Je reviens vers toi car quelque chose me gène du coté du dataSource 'liste'.
    Je ne comprend pas trop pourquoi tu me dis qu'il est correct enfin j'ai l'impression qu'il n'est pas vraiment synchronisé à  mon tableView.

    A aucun moment je n'ai fait [self.tableView setDataSource:liste]; ce qui explique pourquoi mon app plante quand je veux lui faire [self.tableView reloadData];

    Car je souhaite rafraichir mon UITableView lorsque je réorganise mes rows. En effet, j'essaie de faire un exercice ou l'utilisateur devra remettre des étapes dans l'ordre. C'est pourquoi je modifie le cell.backgroundView=imageCellOK quand la row est bien située et cell.backgroundView=imageCellBAD quand elle ne l'est pas. J'ai donc besoin de recharger tout le UITableView car la modification d'une ligne peut décaler l'ensemble des autres étapes ce qui aura pour conséquence de modifier leur image de fond en imageCellOK si elles ne retrouvent au bon endroit.

    Une fois que tout ceci fonctionnera il me restera plus qu'à  mélanger mon tableau et de laisse l'utilisateur le remettre en ordre en manipulant les rows de mon UITableView.

    Voici mon code à  jour avec des commentaires
    > aux endroits qui concernent ma question.
    <br />#import &quot;tabViewController.h&quot;<br />#import &quot;elementList.h&quot;<br />#import &quot;XMLToObjectParser.h&quot;<br /><br />@implementation tabViewController<br />@synthesize liste;<br /><br />- (void)viewDidLoad {<br />	[super viewDidLoad];<br />	//Presentation tableView<br />	[self.tableView setSeparatorColor:[UIColor redColor]];<br />	self.title = @&quot;Etapes de la Création d&#39;Entreprise&quot;;<br />	[self.tableView setEditing:YES animated:YES];<br />	<br />	//Adresse du XML<br />	NSString *monURL = [[NSBundle mainBundle] pathForResource:@&quot;draglist&quot; ofType:@&quot;xml&quot;];<br />	NSURL *url = [NSURL fileURLWithPath:monURL];<br />	<br />	//Activation du Parser<br />	XMLToObjectParser *mylistParser = [[[XMLToObjectParser alloc] init] parseXMLAtURL:url toObject:@&quot;elementList&quot; parseError:nil];<br />	<br />	// Enregistrement des résultats<br />	self.liste = mylistParser.items;<br />	[mylistParser release];<br />} <br /><br /><br />#pragma mark Table view methods<br /><br />// Customize the number of rows in the table view.<br />- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {<br />    return [self.liste count];<br />}<br /><br /><br />// Customize the appearance of table view cells.<br />- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {<br />	<br />	static NSString *CellIdentifier = @&quot;Cell&quot;;<br />	<br />	//on réutilise une cell déjà  créee<br />		UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];<br />	<br />	//creation des composants de la cell<br />	CGRect contentRect = CGRectMake(30, 0.0, 260, 43);<br />	UILabel *textCell = nil;<br />	<br />	if(cell == nil) <br />	{<br />		NSLog(@&quot;nouvelle cell : %d&quot;, indexPath.row+1);<br />		<br />		//creation de la cell<br />		cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];<br />		cell.clearsContextBeforeDrawing;<br />		<br />		//on ajoute un label<br />		textCell = [[UILabel alloc] initWithFrame:contentRect];<br />		textCell.clearsContextBeforeDrawing;<br />		textCell.numberOfLines =  3;<br />		textCell.font = [UIFont fontWithName:@&quot;Helvetica&quot; size:12];<br />		textCell.textColor = [UIColor blackColor];<br />		textCell.backgroundColor = [UIColor clearColor];<br />		textCell.opaque = YES;<br />		<br />		textCell.text = [[self.liste objectAtIndex:indexPath.row] libelle];<br />		<br />		//on ajoute la vue comme contentView de la cell<br />		textCell.tag = 1;<br />		[cell.contentView addSubview:textCell];<br />		[textCell release];<br />	}<br />	<br />	//on modifie le text du label<br />	textCell = [cell.contentView viewWithTag:1];<br />	textCell.text = [[self.liste objectAtIndex:indexPath.row] libelle];<br /><br />//-----------------------&gt;<br />//C&#39;est ce test qui permet de changer l&#39;image de fond de la cell	<br />		//on modifie l&#39;image de fond en fonction de la situation de la cell<br />	 NSString* position = [NSString stringWithFormat:@&quot;%d&quot;, indexPath.row+1];<br />	 NSString *resultat = [[liste objectAtIndex:indexPath.row] ordre];<br />	if([position isEqual:resultat])	{<br />		UIImage *imageCellOK = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@&quot;fond_cellule_green&quot; ofType:@&quot;png&quot;]];<br />		UIImageView *viewImageCell = [[UIImageView alloc] initWithImage:imageCellOK];<br />		cell.backgroundView = viewImageCell;<br />	[viewImageCell release];	}<br />	else {<br />		UIImage *imageCellBad = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@&quot;fond_cellule_red&quot; ofType:@&quot;png&quot;]];<br />		UIImageView *viewImageCell = [[UIImageView alloc] initWithImage:imageCellBad];<br />		cell.backgroundView = viewImageCell;<br />	[viewImageCell release];	}<br /><br />//&lt;---------------------------------<br /><br />	return cell;<br />}<br /><br />// Modification du style EDIT en retirant la possibilité de supprimer la row (juste les réorganiser)<br />- (UITableViewCellEditingStyle) tableView:(UITableView *) tableView editingStyleForRowAtIndexPath:(NSIndexPath *) indexPath {<br />	return UITableViewCellEditingStyleNone;<br />}<br /><br /><br />- (void) tableView: (UITableView *) tableView moveRowAtIndexPath: (NSIndexPath *) oldPath toIndexPath: (NSIndexPath *) newPath{<br />	<br />	NSObject *element = [[liste objectAtIndex:[oldPath row]] retain];<br />	[liste removeObjectAtIndex:[oldPath row]];<br />	[liste insertObject:element atIndex:[newPath row]];<br />	<br />	for(int i = 0; i &lt; [liste count]; i++){<br />		NSLog(@&quot;ordre: %@&#092;n&quot;, [[liste objectAtIndex:i] ordre]);<br />	}<br />	<br />//----------------------&gt; <br />//C&#39;est ici qu&#39;il faudrait appeler de nouveau la méthode CellForRowsAtIndexPath pour tout redessiner<br />	//on rafraichis les cells<br />	//[self.tableView setNeedsDisplay];<br />	//[self.tableView reloadData];<br />//&lt;----------------------<br />}<br /><br />- (void)didReceiveMemoryWarning {<br />    [super didReceiveMemoryWarning]; // Releases the view if it doesn&#39;t have a superview<br />    // Release anything that&#39;s not essential, such as cached data<br />}<br /><br />- (void)dealloc {<br />    [super dealloc];<br />}<br /><br /><br />@end<br />
    


    ps: tu remarqueras que j'ai corrigé libelle :p
  • AliGatorAliGator Membre, Modérateur
    20:06 modifié #18
    Quel est le problème ? Ca plante, ou ça n'affiche juste pas tes "rows" ?

    En fait c'est pas vraiment "self.liste" qui est ton dataSource, mais plutôt l'objet dans lequel tu as implémenté tes méthodes tableView:cellForRowAtIndexPath: et consoeurs. Ce dataSource utilise l'objet mis dans self.liste pour y puiser ses données, mais pour être strict, c'est l'objet implémentant le UITableViewDataSource qui est dataSource justement.

    Après, tu n'as pas à  faire [self.tableView setDataSource:liste] mais plutôt aurais à  faire [self.tableView setDataSource:self], car c'est "self" qui répond aux méthodes du protocole UITableViewDataSource que sont "tableView:cellForRowAtIndexPath:" & co. La définition de quel objet est ton dataSource, c'est simple en fait : c'est l'objet qui va implémenter les méthodes de UITableViewDataSource, tout simplement. Et dans l'implémentation de cette méthode, après, tu puises tes données d'où tu veux, mais il est courant de puiser les données dans un NSArray comme toi ici "liste", et de se servir de ces données pour configurer le UITableViewCell demandé avant de le retourner.


    Maintenant, tu n'as sans doute pas à  appeller la méthode [self.tableView setDataSource:self], enfin y'a des chances si tu as bien fait les choses dans IB, car cette affectation du dataSource à  ta tableView est bien souvent faite en effectuant une connection (via un ctrl + glisser) de ta tableView vers son dataSource (et validant quand il te propose d'affecter ce "lien" à  la variable "dataSource", ce qui réalisera, quand ton XIB sera ouvert par ton code, l'affectation de la variable d'instance (et IBOutlet) "dataSource" de ta tableView vers l'objet vers lequel tu as fait glisser dans IB.
  • bnkbnk Membre
    20:06 modifié #19
    En effet j'avais fait pointer tableView comme DataSource sous IB, il doit donc faire le lien automatiquement.

    En plaçant [self.tableView setDataSource:liste]; j'ai un warning: class NSMutableArray dos not implement the 'UITableViewDatasource' protocol.
    Si je buil&go j'obtiens un crash avec ceci en console:
    2009-04-21 23:24:50.160 draglist[28199:20b] *** Terminating app due to uncaught exception &#39;NSInvalidArgumentException&#39;, reason: &#39;*** -[NSCFArray tableView:numberOfRowsInSection:]: unrecognized selector sent to instance 0x524150&#39;
    

    Peut-être faut-il que je mette un NSArray classique comme dataSource mais bon le NSMutableArray est necessaire ici.

    Et mon application plante quand je place: [self.tableView reloadData]; dans moveRowAtIndexPath.
    <br />2009-04-21 23:26:17.938 draglist[28238:20b] *** Terminating app due to uncaught exception &#39;NSRangeException&#39;, reason: &#39;*** -[NSCFArray objectAtIndex:]: index (1) beyond bounds (1)&#39;
    


    merci pour tes explications.
  • AliGatorAliGator Membre, Modérateur
    20:06 modifié #20
    Bah heu... relis mon précédent post, je t'explique justement tout, je vais pas la refaire 50 fois...

    En fait c'est pas "liste" qui est ton dataSource : le dataSource, c'est l'objet Cocoa dans lequel tu implémentes les méthodes du protocole UITableViewDataSource. C'est à  dire l'objet à  qui va être envoyé les message du genre "tableView:... numberOfRowsInSection:..." (et le dataSource doit répondre à  cette méhode en retournant du coup la valeur appropriée.

    Après si tu utilises un NSArray* liste pour savoir quoi retourner, la méthode "tableView:numberOfRowsInSection:" de ton objet dataSource va retourner le nombre d'éléments dans ce NSArray... Mais rien ne t'empêche de te passer d'un NSArray et à  la place de retourner tout en dur :
    -(NSUInteger)tableView:(UITableView*)aTableView numberOfRowsInSection:(NSUInteger) { return 3; }<br />-(UITableViewCell*)tableView:(UITableView*)aTableView cellForRowAtIndexPath:(NSIndexPath*)indexPath {<br />&nbsp; UITableViewCell* cell = [aTableView dequeueReusableCellWithIdentifier:CellIdentifier:@&quot;toto&quot;];<br />&nbsp; if (!cell) cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero @&quot;toto&quot;] autorelease];<br />&nbsp; cell.text = @&quot;Bonjour&quot;;<br />&nbsp; return cell;<br />}
    
    Dans cet exemple de code bateau, j'implémente le protocole UITableViewDataSource... sans utiliser une NSArray sous le capot. Alors bien sûr c'est pas terrible en pratique, car dans mon exemple j'ai mis du texte en dur dans mon code, et quelle que soit la cellule (ligne) demandée, je leur met toutes le même texte... Mais n'empêche que si j'implémente ces méthodes dans ma classe MaSource, cette classe va donc se conformer au protocole UITableViewDataSource (cf la doc pour ce que signifie le terme "se conformer à  un protocole si tu ne sais pas) et donc pourra servir de dataSource valide. Alors qu'il n'utilise aucun NSArray, c'est pas le NSArray la dataSource, c'est bien l'objet de type "MaSource", puisque pour qu'un objet puisse être dataSource d'une TableView il faut qu'il implémente les méthodes du protocole (qu'il se conforme au protocole) UITableViewDataSource (tu me copieras ça 100 fois ;))


    Après, que ton dataSource utilise un NSArray pour savoir quelle valeurs retourner à  la tableView quand elle demande une cellule, ou que tu mettes les données en dur, ou que tu utilises un tableau C, ou un NSDictionary, ou tout ce que tu veux, la tableView elle s'en fout comme de l'an quarante : elle se contente quelque part dans son code à  elle sous le capot de faire un appel du genre [tt][dataSource tableView:self cellForRowAtIndexPath:requestedIndexPath];[/tt] quand elle a besoin d'une cellule, donc ça va appeler la méthode "tableView:cellForRowAtIndexPath:" de l'objet qui a été défini comme dataSource de ladite tableView, objet qui est donc sensé répondre à  ce message.

    Or liste, qui est un NSArray, ne sait pas répondre à  ce message, va pas t'entêter à  le mettre en dataSource !
  • AliGatorAliGator Membre, Modérateur
    20:06 modifié #21
    En tout cas je ne peux que te conseiller de lire la doc ici :
    http://developer.apple.com/iPhone/library/documentation/UserExperience/Conceptual/TableView_iPhone/Introduction/Introduction.html

    Et ici pour ce qui est du principe des protocoles : http://developer.apple.com/DOCUMENTATION/Cocoa/Conceptual/ObjectiveC/Articles/ocProtocols.html

    Tu as aussi éventuellement cette doc : attention c'est pour les NSTableView donc pour quand tu programmes pour MacOSX et pas pour iPhone, les méthodes utilisées par les NSTableViews sont un peu différentes et n'ont pas la même signature... mais au moins ça t'explique le principe, même s'il faut adapter aux classes de l'iPhone ensuite bien sûr.
  • bnkbnk Membre
    avril 2009 modifié #22
    Toutes mes excuses pour mon message complètement à  coté de la plaque d'hier.

    J'ai parcourus la doc que tu m'as conseillé en m'attardant plus sur le principe des protocoles.

    Mais je n'ai pas encore compris pourquoi mon: [self.tableView reloadData]; dans moveRowAtIndexPath plante.
    2009-04-22 09:54:22.894 draglist[28716:20b] *** Terminating app due to uncaught exception &#39;NSRangeException&#39;, reason: &#39;*** -[NSCFArray insertObject:atIndex:]: index (3) beyond bounds (1)&#39;
    


    L'idée est de rafraichir le tableView afin de changer mon cell.backgroundView si mes rows sont bien placées ou non. Ceci fonctionne pour mes rows #1, #2 et #9 #10 car elles sont rafraichies si je les masque et réaffiche en scrollant mais celles du milieu de mon tableView ne le sont jamais puisque toujours affichées.
  • AliGatorAliGator Membre, Modérateur
    avril 2009 modifié #23
    Bon eh bien je pense que ta mise en place de ton dataSource est OK (tu as bien défini ton objet en tant que dataSource, la méthode est bien appellée... Maintenant c'est semble-t-il dans ton code de tableView:cellForRowAtIndexPath: que tu effectues des opérations qui se trouvent faire planter ton appli.
    En particulier, d'après l'erreur que tu mentionnes, tu essayes d'insérer un élément à  l'emplacement 3 de ton tableau (j'imagine qu'il s'agit de ton NSArray "liste" pour le coup) alors que cet emplacement ne contient qu'un seul élément.
    Or la méthode insertObject:atIndex: nécessite que l'index passé soit un index valide. Si tu veux insérer une valeur dans la "case" 20 d'un tableau qui n'a que 10 "cases", il te faudra rajouter 9 cases (avec le contenu de ton choix dedans, selon les besoins) avant de pouvoir ajouter la case n°20, par exemple... et encore si tu as ce cas là  c'est que tu as sans doute plutôt oublié un cas auquel tu n'aurais pas pensé... car tel que tu me décris ton besoin (avoir un tableau dont tu réarranges les lignes), normalement tu devrais déjà  avoir le bon nombre de lignes, le but est juste de les réarranger...


    D'ailleurs pour ce type de besoin, à  savoir réarranger l'ordre des éléments d'un tableau, si tu vas dans la doc de NSMutableArray, tu as une section précisément dédiée à  ça qui liste quelques méthodes pour ce genre de tâche. C'est ainsi qu'on trouve une méthode [tt]- (void)exchangeObjectAtIndex:(NSUInteger)idx1 withObjectAtIndex:(NSUInteger)idx2[/tt] qui pourrait peut-être être plus appropriée à  tes besoins ?

    Mais sinon, si le but c'est non pas d'échanger la position de 2 éléments mais plutôt de prendre l'élément à  l'index idx1 et le mettre à  l'index idx2 à  la place... décalant tous les autres en conséquence... Alors il faut faire attention au décalage d'indice.
    Car l'idée alors typiquement c'est d'enlever ton objet du tableau, puis de le réinsérer à  la nouvelle position... mais pour ça il faut faire attention à  plusieurs choses :
    • Une fois que tu as récupéré ton objet à  l'index idx1 dans ton tableau, et avant de l'enlever dudit tableau, il faut penser à  faire un "retain" sur l'objet... car l'enlever du tableau va baisser son retainCount de 1 forcément (même si peu après quand tu vas le réinsérer dans le tableau qques lignes plus loin il va remonter à  sa valeur initiale). Du coup pendant cette manipulation l'objet pourrait potentiellement être supprimé de la mémoire si seul le tableau le "retain"-ait et que le retirer du tableau fait descendre son retainCount à  zéro...
    • Quand tu vas réinsérer ton élément dans ton tableau, attention au décalage d'indice, puisque dans le cas où idx2 > idx1, alors le fait d'avoir à  l'instant supprimé ton objet de ton NSArray fait que tous les objets "qui sont après" dans le NSArray "liste" ont été décalé d'un cran, leur index ayant été décrémenté du coup. Du coup au moment de l'insertion à  la nouvelle position dans ton NSArray, il faut penser à  prendre en considération ce décalage et insérer à  idx2-1 et non idx2. Par contre si idx2 < idx1, le décalage des objets ayant lieu pour les index après idx1, pas besoin d'altérer idx2.
  • bnkbnk Membre
    20:06 modifié #24
    Je n'effectue aucune insertion dans mon tableau liste dans la méthode cellForRowAtIndexPath c'est uniquement dans ma méthode moveRowAtIndexPath.
    Cette erreur apparait uniquement si je décommente mon [self.tableView reloadData]; (dans le but de rafraichir mon tabview) en fin de méthode moveRowAtIndexPath.

    Mon but n'est pas d'échanger la position de 2 élément mais de changer d'index l'élément déplacé.

    * Concernant la réorganisation de mon tableau 'liste' et du retain de l'objet c'est fait comme ceci:
    NSObject *element = [[liste objectAtIndex:[oldPath row]] retain];<br />	[liste removeObjectAtIndex:[oldPath row]];<br />	[liste insertObject:element atIndex:[newPath row]];<br />	[element release];
    


    Ce qui a pour effet de bien modifier mon tableau 'liste' lorsque je le contrôle avec:
    for(int i = 0; i &lt; [liste count]; i++){<br />		NSLog(@&quot;ordre: %@&#092;n&quot;, [[liste objectAtIndex:i] ordre]);<br />	}<br />
    


    'Ordre' étant une des deux variables de mes objets 'element' contenus dans le tableau 'liste' (element possède deux variables: 'ordre' et 'libelle').

    Si je place ma row#1 en row #3, j'obtiens bien le log:
    <br />2009-04-22 11:13:06.128 draglist[29588:20b] ordre: 2<br />2009-04-22 11:13:06.128 draglist[29588:20b] ordre: 3<br />2009-04-22 11:13:06.129 draglist[29588:20b] ordre: 1<br />2009-04-22 11:13:06.129 draglist[29588:20b] ordre: 4<br />2009-04-22 11:13:06.129 draglist[29588:20b] ordre: 5<br />2009-04-22 11:13:06.130 draglist[29588:20b] ordre: 6<br />2009-04-22 11:13:06.130 draglist[29588:20b] ordre: 7<br />2009-04-22 11:13:06.130 draglist[29588:20b] ordre: 8<br />2009-04-22 11:13:06.131 draglist[29588:20b] ordre: 9<br />2009-04-22 11:13:06.131 draglist[29588:20b] ordre: 10<br />
    


    Donc mon element à  l'indice 0 passe à  l'index 2, je souhaite juste tester si mon indice+1 correspond bien à  mon ordre si c'est le cas je change la cell.backgroundView = imageCellOK.
    Dans ce cas j'aurai pour les rows #4 à  #10 imageCellOK et les rows #1 à  #3 imageCellBAD.

    Si il y a une différence entre index et indice, il va donc falloir que je le précise au moment ou je modifie mon tab 'liste'..comment?

    <br />#import &quot;tabViewController.h&quot;<br />#import &quot;elementList.h&quot;<br />#import &quot;XMLToObjectParser.h&quot;<br /><br />@implementation tabViewController<br />@synthesize liste;<br /><br />- (void)viewDidLoad {<br />	[super viewDidLoad];<br />	//Presentation tableView<br />	[self.tableView setSeparatorColor:[UIColor redColor]];<br />	//[self.tableView setBackgroundColor:[UIColor lightGrayColor]];<br />	self.title = @&quot;Etapes de la Création d&#39;Entreprise&quot;;<br />	[self.tableView setEditing:YES animated:YES];<br />	<br />	//Adresse du XML<br />	NSString *monURL = [[NSBundle mainBundle] pathForResource:@&quot;draglist&quot; ofType:@&quot;xml&quot;];<br />	NSURL *url = [NSURL fileURLWithPath:monURL];<br />	<br />	//Activation du Parser<br />	XMLToObjectParser *mylistParser = [[[XMLToObjectParser alloc] init] parseXMLAtURL:url toObject:@&quot;elementList&quot; parseError:nil];<br />	<br />	// Enregistrement des résultats<br />	self.liste = mylistParser.items;<br />	<br />	/*	 //Test résultat en console<br />	 for(int i = 0; i &lt; [[mylistParser items] count]; i++){<br />	 NSLog(@&quot;ordre element %@&quot;, [[[mylistParser items] objectAtIndex:i] ordre]);<br />	 NSLog(@&quot;libelle %@&quot;, [[[mylistParser items] objectAtIndex:i] libelle]);<br />	 NSLog(@&quot;ordre element (list)%@&quot;, [[self.liste objectAtIndex:i] ordre]);<br />	 NSLog(@&quot;ordre element (list)%@&quot;, [[self.liste objectAtIndex:i] libelle]);<br />	 }<br />	 */<br />	[self.tableView setDataSource:self];<br />	<br />	[mylistParser release];<br />} <br /><br /><br />#pragma mark Table view methods<br /><br />// Customize the number of rows in the table view.<br />- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {<br />    return [self.liste count];<br />}<br /><br /><br />// Customize the appearance of table view cells.<br />- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {<br />	<br />	static NSString *CellIdentifier = @&quot;Cell&quot;;<br />	<br />	//on réutilise une cell déjà  créee<br />	UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];<br />	//cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];<br />	<br />	//creation des composants de la cell<br />	CGRect contentRect = CGRectMake(30, 0.0, 260, 43); //(14)0, 0, 280, 43<br />	UILabel *textCell = nil;<br />	<br />	if(cell == nil) <br />	{<br />		NSLog(@&quot;nouvelle cell : %d&quot;, indexPath.row+1);<br />		<br />		//creation de la cell<br />		cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];<br />		<br />		//cell.text = [[self.liste objectAtIndex:indexPath.row] libelle];<br />		//cell.font = [UIFont fontWithName:@&quot;Helvetica&quot; size:12];<br />		//cell.image = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@&quot;imageCell&quot; ofType:@&quot;png&quot;]];<br />		<br />		cell.clearsContextBeforeDrawing;<br />		<br />		//on ajoute un label<br />		textCell = [[UILabel alloc] initWithFrame:contentRect];<br />		textCell.clearsContextBeforeDrawing;<br />		textCell.numberOfLines =  3;<br />		textCell.font = [UIFont fontWithName:@&quot;Helvetica&quot; size:12];<br />		textCell.textColor = [UIColor blackColor];<br />		textCell.backgroundColor = [UIColor clearColor];<br />		textCell.opaque = YES;<br />		<br />		textCell.text = [[self.liste objectAtIndex:indexPath.row] libelle];<br />		<br />		//on ajoute la vue comme contentView de la cell<br />		textCell.tag = 1;<br />		[cell.contentView addSubview:textCell];<br />		[textCell release];<br />	}<br />	<br />	/*NSLog(@&quot;cell existante: %d&quot;, indexPath.row+1);<br />	NSLog(@&quot;libelle: %@&quot;, [[self.liste objectAtIndex:indexPath.row] libelle]);<br />	NSLog(@&quot;ma view tag %@&quot;,[cell viewWithTag:1]);*/<br />	<br />	//on modifie le text du label<br />	textCell = [cell.contentView viewWithTag:1];<br />	textCell.text = [[self.liste objectAtIndex:indexPath.row] libelle];<br />	<br />		//on modifie l&#39;image de fond en fonction de la situation de la cell<br />	 NSString* position = [NSString stringWithFormat:@&quot;%d&quot;, indexPath.row+1];<br />	 NSString *resultat = [[liste objectAtIndex:indexPath.row] ordre];<br />	if([position isEqual:resultat])	{<br />		UIImage *imageCellOK = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@&quot;fond_cellule_green&quot; ofType:@&quot;png&quot;]];<br />		UIImageView *viewImageCell = [[UIImageView alloc] initWithImage:imageCellOK];<br />		cell.backgroundView = viewImageCell;<br />	[viewImageCell release];	}<br />	else {<br />		UIImage *imageCellBad = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@&quot;fond_cellule_red&quot; ofType:@&quot;png&quot;]];<br />		UIImageView *viewImageCell = [[UIImageView alloc] initWithImage:imageCellBad];<br />		cell.backgroundView = viewImageCell;<br />	[viewImageCell release];	}<br />	<br />	return cell;<br />}<br /><br />// Modification du style EDIT en retirant la possibilité de supprimer la row (juste les réorganiser)<br />- (UITableViewCellEditingStyle) tableView:(UITableView *) tableView editingStyleForRowAtIndexPath:(NSIndexPath *) indexPath {<br />	return UITableViewCellEditingStyleNone;<br />}<br /><br /><br />- (void) tableView: (UITableView *) tableView moveRowAtIndexPath: (NSIndexPath *) oldPath toIndexPath: (NSIndexPath *) newPath{<br />	<br />	NSObject *element = [[liste objectAtIndex:[oldPath row]] retain];<br />	[liste removeObjectAtIndex:[oldPath row]];<br />	[liste insertObject:element atIndex:[newPath row]];<br />	[element release];<br />	<br />	//test si il est bien placé<br />	<br />	NSLog(@&quot;NSIndexPath: %d&#092;n&quot;, oldPath);<br />	NSLog(@&quot;NSIndexPath: %d&#092;n&quot;, newPath);<br />	NSLog(@&quot;old path: %d&#092;n&quot;, [oldPath row] +1);<br />	NSLog(@&quot;new path: %d&#092;n&quot;, [newPath row] +1);<br />	NSLog(@&quot;perfect path: %@&#092;n&quot;, [[liste objectAtIndex:[oldPath row]] ordre]);<br />	<br />	for(int i = 0; i &lt; [liste count]; i++){<br />		NSLog(@&quot;ordre: %@&#092;n&quot;, [[liste objectAtIndex:i] ordre]);<br />	}<br />	<br />	//on redessine les cells affichées<br />	//[self.tableView setNeedsDisplay];<br />	//----------------------&gt; &nbsp; <br />	[self.tableView reloadData];<br />}<br /><br />- (void)didReceiveMemoryWarning {<br />    [super didReceiveMemoryWarning]; // Releases the view if it doesn&#39;t have a superview<br />    // Release anything that&#39;s not essential, such as cached data<br />}<br /><br />- (void)dealloc {<br />    [super dealloc];<br />}<br /><br /><br />@end<br />
    
  • AliGatorAliGator Membre, Modérateur
    20:06 modifié #25
    Oui évidemment je parlais de ta méthode moveRowAtIndexPath etc... je te répond vite fait (déjà  que j'en fais des laà¯us pour t'expliquer plein de trucs, tu vas qd mm pas m'en vouloir si je fait une petite erreur dans le tout nan mais :D)

    Donc il semble que ton action de réordonnancement de ta liste fonctionne, d'après ton log fait dans ta boucle for. Reste plus qu'à  mettre des NSLog et des breakpoints dans ton cellForRowAtIndexPath: ou tes autres méthodes de dataSource pour voir à  quelle ligne ça plante. Apparamment de ce que tu me dis comme erreur qd ça crash, c'est au moment d'un insertObjectAtIndex je crois, c'est ça ? donc ça doit être autour d'une ligne qui fait ça, c'est pour ça que je pensais que c'était au moment de ton réordonnancement de ton NSArray. Mais si ça ne crash que quand tu fais reloadData, c'est plus probablement dans une méthode du dataSource fournissant les données.

    Je te laisse mettre des logs un peu partout pour isoler le problème et voir autour de quelle(s) ligne(s) ça plante, là  je peux pas faire grand chose d'ici la balle est dans ton camp pour débuguer ça et voir quel est le code fautif (et c'est un très bon exercice)
  • bnkbnk Membre
    20:06 modifié #26
    J'ai mis des breakpoints et NSLog un peu partout.
    Un qui pointe sur: [self.tableView reloadData]; et un sur le NSLog juste en dessous.

    - (void) tableView: (UITableView *) tableView moveRowAtIndexPath: (NSIndexPath *) oldPath toIndexPath: (NSIndexPath *) newPath{<br />	<br />	NSObject *element = [[liste objectAtIndex:[oldPath row]] retain];<br />	[liste removeObjectAtIndex:[oldPath row]];<br />	[liste insertObject:element atIndex:[newPath row]];<br />	[element release];<br />	<br />	//test si il est bien placé	<br />	for(int i = 0; i &lt; [liste count]; i++){<br />		NSLog(@&quot;ordre: %@&#092;n&quot;, [[liste objectAtIndex:i] ordre]);<br />	}<br />	<br />	//on redessine les cells affichées<br />	[self.tableView reloadData];<br />	NSLog(@&quot;coucou&quot;);<br />	<br />}
    


    Lorsque j'exécute mon programme et que je fais continue le reloadData passe, le NSlog s'affiche puis en fin de méthode ça crash. Je ne sais pas ou est-ce que le code continue de s'exécuter pour que ça plante comme ça car aucun autre NSLog ne sort.

    Une autre solution serait de parcourir toutes mes cells, de faire le test pour voir si elles sont positionnées à  la bonne ligne et changer la background view comme ça.. mais comment parcourir toutes mes cells affichées et changer leur backgroundView dynamiquement?
  • bnkbnk Membre
    20:06 modifié #27
    Bon je n'ai pas résolu ce problème mais je le contourne.
    C'est bon mes cellules se mettent à  jour automatiquement quand je déplace mes rows en appelant ma méthode cellForRowAtIndexPath et en lui passant en argument les deux path que je bouge dans mon moveRowAtIndexPath de ce fait, je recupère ma cellule et je refais le test pour lui attribuer la backgroundView OK ou BAD.

    code permettant d'appeler la méthode moveRowAtIndexPath.
    <br />UITableViewCell *cell = [self.tableView cellForRowAtIndexPath: oldPath];<br />
    


    Il n'est pas exclu que je fasse de nouveau appel à  tes conseils et explications mais merci beaucoup pour ton investissement !
  • bnkbnk Membre
    mai 2009 modifié #28
    Bonjour,

    Je pensais en avoir terminé mais voilà  que je découvre un un petit bug(?) qui m'agace le voici en vidéo:

    http://bnka.free.fr/beurdel/dragDrop.mov

    Comme vous pouvez voir lorsque je déplace une row, je change l'image de fond de la cellule en vert ou rouge en fonction de la position (si elle est bien placée elle devient verte).

    En console le changement de position est instantané si je bouge la row1 en position #1 en position #3 j'obtiens bien comme ordre row2, row3, row1 (tien n'est bien placé c'est rouge normal).

    Si je remonte ma row1 en position #2, j'ai bien en console row2, row1, row3 mais à  l'affichage j'ai rouge vert rouge... (là  ce n'est pas normal).

    Si je rebouge ma row1 en position #2 sans la lacher (histoire de refresh le tableView) j'obtiens rouge, rouge, vert (retour à  la normal).

    Comment faire pour refresh automatiquement ou simuler un second déplacement dans la même position pour que mes fond de cellules se colorient comme il faut?


    Voici le code:

    <br /><br />- (void) tableView: (UITableView *) tableView moveRowAtIndexPath: (NSIndexPath *) oldPath toIndexPath: (NSIndexPath *) newPath{<br />	dejaMove = YES;<br />	NSObject *element = [[liste objectAtIndex:[oldPath row]] retain];<br />	[liste removeObjectAtIndex:[oldPath row]];<br />	[liste insertObject:element atIndex:[newPath row]];<br />	[element release];<br />	<br />	for(int i = 0; i &lt; [liste count]; i++){<br />		NSLog(@&quot;ordre element (list)%@&quot;, [[self.liste objectAtIndex:i] ordre]);<br />	}<br />	<br />	[self refreshBackgroundCell];<br />	<br />}<br /><br />- (void) refreshBackgroundCell {<br />	printf(&quot;on refresh&#092;n&quot;);<br />	//on actualise les rows en testant la position des cell et en modifiant leur image de fond<br />	NSIndexPath *monPath = nil;<br />	NSString *maPosition = nil;<br />	NSString *monOrdre = nil;<br />	UIImage *imageCellOK = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@&quot;fond_cellule_green&quot; ofType:@&quot;png&quot;]];<br />	UIImage *imageCellBad = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@&quot;fond_cellule_red&quot; ofType:@&quot;png&quot;]];<br />	UIImageView *viewImageCell = nil;<br />	UITableViewCell *maCell = nil;<br />	<br />	for(int i = 0; i &lt; [liste count]; i++){<br />		monPath = [NSIndexPath indexPathForRow:i inSection:0];<br />		maPosition = [NSString stringWithFormat:@&quot;%d&quot;, monPath.row+1];<br />		monOrdre = [[liste objectAtIndex:monPath.row] ordre];<br />		maCell = [self.tableView cellForRowAtIndexPath: monPath];<br />		<br />		//NSLog(@&quot;maposition: %@&quot;, maPosition);<br />		//NSLog(@&quot;monOrdre: %@&quot;, monOrdre);<br />		<br />		if([maPosition isEqual:monOrdre])	{<br />			viewImageCell = [[UIImageView alloc] initWithImage:imageCellOK];<br />			//[maCell setEditing:NO];<br />		}<br />		else {<br />			viewImageCell = [[UIImageView alloc] initWithImage:imageCellBad];<br />		}<br />		maCell.backgroundView = viewImageCell;<br />		[viewImageCell release];<br />	}<br />	//[self.tableView setNeedsDisplay];<br />}<br />
    


    J'ai essayé mais pas moyen de lui faire entendre raison, si quelqu'un à  une idée...
    Merci !
  • bnkbnk Membre
    20:06 modifié #29
    Résolu !

    Personne n'est venu à  mon secours grrr :p
    J'ai quand même trouvé une solution, ce n'est surement pas le mieux à  faire mais comme je ne voyais vraiment pas ou étais le problème, j'ai mis un timer histoire de le laisser souffler avant de lancer mon test pour colorier mes Cells et là  ça fonctionne!

    Voici la solution:

    <br />- (void) tableView: (UITableView *) tableView moveRowAtIndexPath: (NSIndexPath *) oldPath toIndexPath: (NSIndexPath *) newPath{<br />	dejaMove = YES;<br />	NSObject *element = [[liste objectAtIndex:[oldPath row]] retain];<br />	[liste removeObjectAtIndex:[oldPath row]];<br />	[liste insertObject:element atIndex:[newPath row]];<br />	[element release];<br />	<br />	for(int i = 0; i &lt; [liste count]; i++){<br />		NSLog(@&quot;ordre element (list)%@&quot;, [[self.liste objectAtIndex:i] ordre]);<br />	}<br />	<br />	NSTimer *timer;<br />	<br />	timer = [NSTimer scheduledTimerWithTimeInterval: 0.5<br />											 target: self<br />										&nbsp;  selector: @selector(waitTorefreshBackgroundCell:)<br />										&nbsp;  userInfo: nil<br />											repeats: NO];<br />	<br />}<br /><br />- (void) waitTorefreshBackgroundCell: (NSTimer *) timer<br />{<br />&nbsp; &nbsp; [self refreshBackgroundCell];<br />}<br />
    
Connectez-vous ou Inscrivez-vous pour répondre.