Comment configurer un NSTableView avec une cellule personnalisée en utilisant un subview
j'essaie de configurer un NSTableView avec une cellule personnalisée utilisant un ArrayController et Bindings . Pour ce faire, j'ai ajouté un subview à la cellule personnalisée. La connexion de données semble fonctionner quelque peu. Cependant, il semble y avoir un problème de reformulation que je ne peux pas résoudre. Lorsque je charge l'application que certaines des cellules sont rendus. Lorsque je fais défiler les lignes ou que j'en sélectionne une, le rendu change.
j'ai créé un exemple de projet sur github pour illustrer ce qu'est le problème.
le code source réel pour le rendu de la cellule peut être trouvé ici :
// CustomCell.m
- (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView*)controlView {
if (![m_view superview]) {
[controlView addSubview:m_view];
}
// The array controller only gets wrapped data items pack by the NSObjectTransformer.
// Therefore, objectValue returns a NSObjectWrapper.
// Unpack the wrapper to retreive the data item.
DataItem* dataItem = [(NSObjectWrapper*)[self objectValue] original];
[[m_view name] setStringValue:dataItem.name];
[[m_view occupation] setStringValue:dataItem.occupation];
[m_view setFrame:cellFrame];
}
il semble que le parent controlView
ne se redessine pas. Puis-je le forcer en quelque sorte?
2 réponses
ce n'est presque certainement pas une bonne façon de faire, et je vais expliquer pourquoi par la suite: cependant, il semble que cela fonctionne. Remplacer la méthode drawInteriorWithFrame:inView:
de votre classe de cellules par la suivante:
- (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView*)controlView {
DataItem* dataItem = [(NSObjectWrapper*)[self objectValue] original];
[[m_view name] setStringValue:dataItem.name];
[[m_view occupation] setStringValue:dataItem.occupation];
[m_view setFrame:cellFrame];
NSData *d = [m_view dataWithPDFInsideRect:[m_view bounds]];
NSImage *i = [[NSImage alloc] initWithData:d];
[i setFlipped:YES];
[i drawInRect:cellFrame fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0];
}
le problème est qu'un seul NSCell est créé pour la table entière. C'est ainsi que les cellules sont censées fonctionner: la vue de table crée une cellule, et appelle setObject... suivi de drawInterior... encore et encore pour obtenir la cellule de dessiner la table entière. C'est génial du point de vue de l'efficacité (la classe NSCell a été conçue à l'époque où 25mhz était un ordinateur rapide, donc elle visait à minimiser le nombre d'objets alloués), mais cela pose des problèmes ici.
dans votre code, vous peuplez une vue avec des valeurs, et définissez son cadre, en l'ajoutant comme un sous-vue de la vue de table si nécessaire. Cependant, puisque vous n'avez qu'une seule instance de NSCell, il ne peut y avoir qu'une seule vue: vous avez pris la seule vue que vous aviez et l'avez simplement déplacée vers le bas des lignes de la table.
pour faire cela correctement, vous avez besoin d'une structure de données pour suivre toutes les vues que vous avez ajoutées en tant que sous-vues de votre vue NSTableView, et quand la cellule met à jour une dans la méthode drawInterior... vous avez besoin de regarder vers le haut qui était le bon et mettre à jour cela. Vous devez également allouer toutes ces vues en code (ou au moins déplacer la vue vers un nib séparé dont vous pouvez charger plusieurs copies), parce que comme il est que vous avez seulement un dans votre nib et la copie d'une vue est un douleur.
le code que j'ai écrit est un kludge, car il est vraiment inefficace. Ce que j'ai fait était chaque fois que la vue doit dessiner, j'ai dessiné la vue dans un tampon d'image hors écran, et puis j'ai dessiné le tampon dans le bon endroit dans la vue de table. Ce faisant, j'ai évité le problème de n'avoir qu'une seule vue, puisque le code prend et tire une nouvelle copie de son contenu chaque fois qu'il est nécessaire.
EDIT: Voir mon autre réponse, pour explication
avez-vous mis en œuvre copyWithZone:
? Vous devez vous assurer que vous copiez ou recréez votre vue dans cette méthode, sinon différentes cellules finiront par partager une vue (parce que NSTableView copie ses cellules).