NSOutlineView shows only first 2 levels of the NSTreeController hierarchy - objective-c

I'm populating a NSOUtlineView with a NSTreeController.
The NSTreeController is a 3 levels hierarchy controller (CBMovie, CBDisc and CBEpisode), but only the first 2 levels are displayed in the outline view.
The implementation is the same for all objects: I've implemented methods to specify children, children count and if the object is a leaf. These methods are correctly called for all objects (also for those ones that are not displayed, the grandchildren: CBEpisode).
In the outline View, everything is displayed correctly for the first 2 level. But grandchildren are never displayed, I don't have the option to expand their parent to see them. I can only see CBMovie and CBDiscs.
I'm wondering if there is another setting I'm missing, about how deep the nodes can expand in NSTreeControllers or NSOutlineView configurations.
Below: Implementation in one of the three nodes.
Each node class has different path to its children. This is specified in the -(NSArray*)children method (correctly called).
-(NSArray*)children
{
return [[self Episodes] allObjects];
}
-(int)childrenCount
{
return [[self Episodes] count];
}
-(BOOL)isLeaf
{
return ![[self Episodes] count];
}
Output of logging code. The datasource, the NSTreeController, seems to have the correct structure.
CBMovie
CBDisc
CBEpisode
CBEpisode
CBMovie
CBDisc
CBDisc
CBDisc
CBDisc
CBMovie
CBDisc
CBEpisode
CBEpisode
This is how I populate the NSOutlineView (cell based). I don't use datasource methods, but I'm binding it programmatically.
NSMutableDictionary *bindingOptions = [[NSMutableDictionary alloc] initWithCapacity:2];
if (metadata.valueTransformer) {
[bindingOptions setObject:metadata.valueTransformer forKey:NSValueTransformerNameBindingOption];
}
[bindingOptions setObject:[NSNumber numberWithBool:NO] forKey:NSCreatesSortDescriptorBindingOption];
[bindingOptions setObject:[NSNumber numberWithBool:NO] forKey:NSRaisesForNotApplicableKeysBindingOption];
[newColumn bind:#"value" toObject:currentItemsArrayController withKeyPath:[NSString stringWithFormat:#"arrangedObjects.%#", metadata.columnBindingKeyPath] options:bindingOptions];

Examining the NSTreeController side
The NSTreeController is a 3 levels hierarchy controller, but only the first 2 levels are displayed in the outline view.
Have you confirmed that all three levels are loaded into your NSTreeController? You could do this by logging its contents to the console with the extension below. If the output produced by this code matches what appears in your outline-view, the problem is probably on the NSTreeController side, not the outline-view.
#import "NSTreeController+Logging.h"
#implementation NSTreeController (Logging)
// Make sure this is declared in the associated header file.
-(void)logWithBlock:(NSString * (^)(NSTreeNode *))block {
NSArray *topNodes = [self.arrangedObjects childNodes];
[self logNodes:topNodes withIndent:#"" usingBlock:block];
}
// For internal use only.
-(void)logNodes:(NSArray *)nodes
withIndent:(NSString *)indent
usingBlock:(NSString * (^)(NSTreeNode *))block {
for (NSTreeNode * each in nodes) {
NSLog(#"%#%#", indent, block(each));
NSString *newIndent = [NSString stringWithFormat:#" %#", indent];
[self logNodes:each.childNodes withIndent:newIndent usingBlock:block];
}
}
#end
The above code does not need to be adjusted, all you need to do is call it with a customised block:
-(void)logIt {
[self.treeController logWithBlock:^NSString *(NSTreeNode * node) {
// This will be called for every node in the tree. This implementation
// will see the object's description logged to the console - you may
// want to do something more elaborate.
NSString *description = [[node representedObject] description];
return description;
}];
}
Examining the NSOutlineView side
If all the data seems to have loaded correctly into the NSTreeController you could have a look at how your NSOutlineView is populated. The delegate method -[NSOutlineViewDelegate outlineView:viewForTableColumn:item] is a good place to start. The item argument is the NSTreeNode instance, so, before you return the relevant view you can look at this node and make sure it's behaving as expected. In your case, you should scrutinize the properties of item objects that are representing CBDisc objects. (You may need to click on disclosure buttons to get this method to fire for the relevant objects.)
-(NSView *)outlineView:(NSOutlineView *)outlineView
viewForTableColumn:(NSTableColumn *)tableColumn
item:(id)item {
NSTreeNode *node = (NSTreeNode *)item;
NSManagedObject *representedObject = (NSManagedObject *)node.representedObject;
// Query the node
NSLog(#"%# <%#>", representedObject.description, [representedObject class]);
NSLog(#" node is a leafNode: %#", node.isLeaf ? #"YES" : #"NO");
NSLog(#" node has child-count of: %lu", (unsigned long)node.childNodes.count);
// Query the NSManagedObject
// your stuff here...
// This is app-specific - you'll probably need to change the identifier.
return [outlineView makeViewWithIdentifier:#"StandardTableCellView" owner:self];
}

So, I've figured out why.
Quite stupid: the main outline column containing the disclosure arrows was 20px width only and the arrows of the children had indentation.
I'm using the outline column for the arrows only and not the titles of the nodes, that's why it's so narrow.
I had disabled indentation and now I can see all arrows.

Related

NSTreeController/NSOutlineView loses its selection

I'm developing a desktop Cocoa application. In the app I have a view-based NSOutlineView binded to an NSTreeController:
The NSTreeController is in entity mode and driven by Core Data. Everything works as expected until the underlaying model graph changes. Whenever a new object inserted into the registered NSManagedObjectContext the NSTreeController refresh its content and the binded NSOutlineView shows the result properly. The content of the controller sorted by "title" with an NSSortDescriptor and I set this sorting during the application startup. The only drawback is that the selectionIndexPath doesn't change even if the preserve selection box is checked in the NSTreeController's preferences. I want to keep the selection on the object that was selected before the new node appeared in the tree.
I've subclassed NSTreeController to debug what's happening with the selection during the change of object graph. I can see that the NSTreeController changes it's content via KVO but the setContent: method doesn't invoked. Than the setSelectionIndexPaths: called via the NSTreeControllerTreeNode KVO but the parameter contains the previous indexPath.
So, to be clear:
Top Level 1
Folder 1-1
Folder 1-2
Top Level 2
Folder 2-1
*Folder 2-3 <== Selected
Folder 2-4
In the initial stage the "Folder 2-3" selected. Then "Folder 2-2" inserted into the NSManagedObjectContext with [NSEntityDescription insertNewObjectForEntityForName:#"Folder" inManagedObjectContext:managedObjectContext];:
Top Level 1
Folder 1-1
Folder 1-2
Top Level 2
Folder 2-1
*Folder 2-2 <== Selected
Folder 2-3
Folder 2-4
I want to keep the selection on "Folder 2-3", hence I've set the "Preseve selection" but it seems that NSTreeController completely ignore this property or I misunderstood something.
How I can force NSTreeController to keep its selection?
UPDATE1:
Unfortunately none of the mutation methods (insertObject:atArrangedObjectIndexPath:, insertObjects:atArrangedObjectIndexPaths: etc.) has ever called in my NSTreeController subclass. I've override most of the factory methods to debug what's going under the hood and that's what I can see when a new managed object inserted into the context:
-[FoldersTreeController observeValueForKeyPath:ofObject:change:context:] // Content observer, registered with: [self addObserver:self forKeyPath:#"content" options:NSKeyValueObservingOptionNew context:nil]
-[FoldersTreeController setSelectionIndexPaths:]
-[FoldersTreeController selectedNodes]
-[FoldersTreeController selectedNodes]
The FoldersTreeController is in entity mode and binded to the managedObjectContext of Application delegate. I have a root entity called "Folders" and it has a property called "children". It's a to-many relationship to an other entity called Subfolders. The Subfolders entity is a subclass of Folders, so it has the same properties as its parent. As you can see on the first attached screenshot the NSTreeController's entity has been set to the Folders entity and it's working as expected. Whenever I insert a new Subfolder into the managedObjectContext it appears in the tree under the proper Folder (as a subnode, sorted by NSSortDescriptor binded to the NSTreeController), but none of the NSTreeController mutation methods are called and if the newly inserted subfolder appears earlier in the list it pulls down everything but the selection remains in the same position.
I can see that the setContent: method is called during the application launch, but that's all. It seems that NSTreeController observe the root nodes (Folders) and reflect model changes somehow via KVO. (So, when I create a new Subfolder and add it to its parent with [folder addChildrenObject:subfolder] it's appearing in the tree, but none of the tree mutation methods are invoked.)
Unfortunately I cannot use the NSTreeController mutation methods directly (add:, addChild:, insert:, insertChild:) because the real applicataion updates the models in a background thread. The background thread uses its own managedObjectContext and merge the changes in batches with mergeChangesFromContextDidSaveNotification. It makes me crazy, because everything is working fine expect the NSOutlineView's selection. When I bunch of Subfolders merged into the main managedObjectContext from the background thread the tree updates itself, but I lost the selection from the object that was selected before the merge.
Update2:
I've prepared a small sample to demonstrate the issue: http://cl.ly/3k371n0c250P
Expand "Folder 1" then select Select "Subfolder 9999"
Press "New subfolder". It will create 50 subfolder in the background operation with batches.
As you can see, the selection will be lost from "Subfolder 9999" even if its saved before the content change in MyTreeController.m
By my reading of the docs and headers, NSTreeController uses NSIndexPaths to store selection. This means that its idea of selection is a chain of indexes into a tree of nested arrays. So as far as it knows, it is preserving the selection in the situation you describe. The problem here is the you're thinking of selection in terms of "object identity" and the tree controller defines selection as "a bunch of indexes into nested array". The behavior you describe is (AFAICT) the expected out-of-the-box behavior for NSTreeController.
If you want selection preservation by object identity, my suggestion would be to subclass NSTreeController and override all mutating methods such that you capture the current selection using -selectedNodes before the mutation, then re-set the selection using -setSelectionIndexPaths: with an array created by asking each formerly selected node for its new -indexPath after the mutation.
In short, if you want behavior other than the stock behavior, you're going to have to write it yourself. I was curious how hard this would be so I took a stab at something that appears to work for the cases I bothered to test. Here 'tis:
#interface SOObjectIdentitySelectionTreeController : NSTreeController
#end
#implementation SOObjectIdentitySelectionTreeController
{
NSArray* mTempSelection;
}
- (void)dealloc
{
[mTempSelection release];
[super dealloc];
}
- (void)p_saveSelection
{
[mTempSelection release];
mTempSelection = [self.selectedNodes copy];
}
- (void)p_restoreSelection
{
NSMutableArray* array = [NSMutableArray array];
for (NSTreeNode* node in mTempSelection)
{
if (node.indexPath.length)
{
[array addObject: node.indexPath];
}
}
[self setSelectionIndexPaths: array];
}
- (void)insertObject:(id)object atArrangedObjectIndexPath:(NSIndexPath *)indexPath
{
[self p_saveSelection];
[super insertObject: object atArrangedObjectIndexPath: indexPath];
[self p_restoreSelection];
}
- (void)insertObjects:(NSArray *)objects atArrangedObjectIndexPaths:(NSArray *)indexPaths
{
[self p_saveSelection];
[super insertObjects:objects atArrangedObjectIndexPaths:indexPaths];
[self p_restoreSelection];
}
- (void)removeObjectAtArrangedObjectIndexPath:(NSIndexPath *)indexPath
{
[self p_saveSelection];
[super removeObjectAtArrangedObjectIndexPath:indexPath];
[self p_restoreSelection];
}
- (void)removeObjectsAtArrangedObjectIndexPaths:(NSArray *)indexPaths
{
[self p_saveSelection];
[super removeObjectsAtArrangedObjectIndexPaths:indexPaths];
[self p_restoreSelection];
}
#end
EDIT: It a little brutal (performance-wise) but I was able to get something working for calls to -setContent: as well. Hope this helps:
- (NSTreeNode*)nodeOfObject: (id)object
{
NSMutableArray* stack = [NSMutableArray arrayWithObject: _rootNode];
while (stack.count)
{
NSTreeNode* node = stack.lastObject;
[stack removeLastObject];
if (node.representedObject == object)
return node;
[stack addObjectsFromArray: node.childNodes];
}
return nil;
}
- (void)setContent:(id)content
{
NSArray* selectedObjects = [[self.selectedObjects copy] autorelease];
[super setContent: content];
NSMutableArray* array = [NSMutableArray array];
for (id object in selectedObjects)
{
NSTreeNode* node = [self nodeOfObject: object];
if (node.indexPath.length)
{
[array addObject: node.indexPath];
}
}
[self setSelectionIndexPaths: array];
}
Of course, this relies on the objects actually being identical. I'm not sure what the guarantees are with respect to CoreData across your (unknown to me) background operation.

How to efficiently flatten a NSOutlineView?

I have a NSOutlineView which is binded to my NSTreeController. The content of the NSTreecontroller (myTreeController) is set with data using the command:
[self.myTreeController setContent:self.myArrayOfFiles];
The content of the array consists of NSTreeNode parent and children objects that are added using a NSTreeNode subclass (TKnode):
[TKnode treeNodeWithRepresentedObject:myRootObject].
This works very well and displays my NSoutlineView correctly. But when I want to iterate over the content of the NSOutlineView I need to flatten it and store the objects in an array. This is where I could need some help as the solution I have is not optimal and potentially prone to errors.
I first return the content of the NSTreeController using:
- (NSArray *)rootNodes;
{
return [[myTreeController arrangedObjects] childNodes] ;
}
This returns a NSTreeControllerTreeNode array of size one that holds the tree structure. I then access the first layer of the tree structure using the childNodes method.
- (NSArray *)flattenedNodes
{
NSMutableArray *mutableArray = [NSMutableArray array];
for (TKnode *rootnode in [self rootNodes]){
for (TKnode *node in [rootnode childNodes]){
if (![[node representedObject] isLeaf]){
[mutableArray addObjectsFromArray:[self descendants:[node representedObject]]];
}
else {
[mutableArray addObject:[[node representedObject] representedObject]];
}
}
}
DLog(#"My files: %lu",[mutableArray count]);
return [[mutableArray copy] autorelease];
}
The children of the children are accessed recursively using the following method:
- (NSArray *)descendants:(TKnode *) node
{
NSMutableArray *descendantsArray = [NSMutableArray array];
for (TKnode *mynode in [node childNodes]) {
[descendantsArray addObject:[mynode representedObject]];
if (!mynode.isLeaf){
[descendantsArray addObjectsFromArray:[self descendants:mynode]];
}
}
return [[descendantsArray copy] autorelease]; // return immutable
}
This works and returns an array containing all of my objects, not the NSTreeNodes, but the objects I am interested in. But it seems error prone that I have to call representedObject sometimes twice on an object to access my object. Is this the way I am supposed to work with NSTReeController and NSTreeNodes? Is there a better way? The reason I wanted to use NSTreeNode and NSTreeController in the first place was to get some of the already implemented methods for free such as sorting and arrangedObjects, which I am used to with NSTableView. But the method I use to access the NSTreeController content does not seem correct ? Should I use the arrangedObjects to get the content of the NSTReeController or should I use a different approach? Any suggestions for how to correctly flatten a NSOutlineView is highly appreciated.
Thanks! Cheers, Trond
This question turned out to be a real tumbleweed (my second in one month) so I wanted to follow up with some more info. I ended up using the above posted code for flattening my NSOutlineView as I was unable to find a better option. When you have to recursively iterate through an unknown number of subfolders (in my case) this seems to be the best option where the descendants method is called for each time you reach a deeper level. I found a very useful website with a number of useful NSTreeController extensions here, which uses the same approach as I had taken. Unless someone helps me figure out a faster and better algorithm for flattening the arrangedObjects of a NSTreeController, I believe i will stick with this approach.
Cheers, Trond
Update: After a question for the solution I decided to post my method for how to flatten an outlineview. I hope his can help others.
- (void) flattenOutlineview
{
NSMutableArray *nodeArray = [NSMutableArray array];
[self.myFlattenedFiles removeAllObjects];
[self.myFlattenedNodes removeAllObjects];
[self.myIndexDict removeAllObjects];
[nodeArray removeAllObjects];
for (SDNode *rootnode in self.rootNodes)
{
[nodeArray addObject:rootnode];
[self.myIndexDict setObject:[rootnode indexPath]
forKey:[[[rootnode representedObject] representedObject] fullpathL]];
for (SDNode *node in [rootnode childNodes])
{
if (node.isLeaf){
[nodeArray addObject:node];
[self.myIndexDict setObject:[node indexPath]
forKey:[[[node representedObject] representedObject] fullPathCopy]];
}
else {
[nodeArray addObjectsFromArray:[self descendants:node]];
[self descendantsIndex:node];
}
}
}
[self.myFlattenedNodes setArray:[nodeArray copy]];
for (SDNode *node in self.myFlattenedNodes)
{
[self.myFlattenedFiles addObject:[[node representedObject] representedObject]];
}
}
Saw this linked from another answer I just wrote. Provided you have everything in the tree expanded/displayed this would work:
for (NSUInteger row = 0; row < [outlineView numberOfRows]; row++)
{
NSTreeNode* node = [outlineView itemAtRow:row];
[nodeArray addObject:node];
}
The rows of the outlineView are basically a flattened tree. And you could perhaps expand every item while iterating to get them all.

With NSTreeController, do I have to manually reload an NSOutlineView when changing the model array?

I have a tree-like model I'd like to show in an NSOutlineView using an NSTreeController.
I was able to set up the bindings and everything works fine as long as I use the NSTreeController's insert and remove functions to change my model tree. If I try to insert or remove from the model tree directly, in some cases the NSOutlineView isn't updating.
If I insert an object into an expanded group of objects, it works:
But if I try to add the first object to a node, that had no children before, nothing happens. The disclosure triangle isn't appearing, so I can't expand it to see the new node.
If I hover over that node with a new object, it is expanded and I can add the second child with no problems. But the triangle is still invisible:
Finally if I close the parent of all these nodes and open them again (triggering a reload) the triangle suddenly appears:
That's why I was wondering if I had to manually reload the NSOutlineView's rows to make the triangle visible, or if I'm messing up something? Thanks!!
UPDATE:
In my Node class I add a new child like this:
- (void)addChild:(MyNode *)child {
[self willChangeValueForKey:#"childNodes"];
[children addObject:child];
[self didChangeValueForKey:#"childNodes"];
}
And I implemented these too (which I set in IB for my NSTreeController):
- (NSArray *)childNodes {
return [NSArray arrayWithArray:children];
}
- (NSInteger)countOfChildNodes {
return [children count];
}
- (BOOL)nodeIsLeaf {
return [children count] < 1;
}
I know that this (especially childNodes) aren't very optimized, but I'm only experimenting at the moment as in the final version my children will be stored in a C array.
UPDATE 2:
I also tried sending KVO notifications for the other 2 properties too, but that didn't help either.
- (void)addChild:(MyNode *)child {
NSLog(#"%#", NSStringFromSelector(_cmd));
[self willChangeValueForKey:#"nodeIsLeaf"];
[self willChangeValueForKey:#"countOfChildNodes"];
[self willChangeValueForKey:#"childNodes"];
[children addObject:child];
[self didChangeValueForKey:#"childNodes"];
[self didChangeValueForKey:#"countOfChildNodes"];
[self didChangeValueForKey:#"nodeIsLeaf"];
}
You have to make sure that all updates to your model are performed in a Key-Value Observing-compliant manner.
Cocoa Bindings Programming Topics: Troubleshooting Cocoa Bindings

Pass data from sheet to insertNewObject in a parent/child relationship

I have a problem to insertNewObject in an entity being the child in a parent/child relationship. It's a CoreData app with a local SQLite base. There are 2 entities presented with 2 TableViews on the main window. Using contentSet, the table for the child will only show data relating to the selected parent.
Adding data to the child is done by showing a sheet with a table of items coming from a 3rd entity. User must pick from this table then click Add. On dismissing the sheet, the child table on main window should be updated with a new row. Problem: nothing appears.
Checking the database content with a third-party app, I see that the new data is there but it doesn't appear on the table view because no info on the relationship with parent was stored, so it doesn't know to which parent it relates.
My code is missing info about this but I just don't see how I should program this. In other words: on dismissing sheet, identify which parent is selected and specify this relationship info when inserting the new data in the child. I would appreciate any help.
Here is my code:
// there are 3 entities: Collectors (parent), CollectedItems (child) and Items.
// we call the sheet presenting the Items list to pick from
- (IBAction)showAddItemDialog:(id)sender {
[NSApp beginSheet:addItemDialog
modalForWindow:window
modalDelegate:self
didEndSelector:#selector(didEndAddItemSheet:returnCode:contextInfo:)
contextInfo:nil];
}
// we dismiss the sheet by clicking on Cancel or Add
- (IBAction)dismissAddItemDialog:(id)sender {
[NSApp endSheet:addItemDialog returnCode:([sender tag])];
[addItemDialog orderOut:sender];
}
// depending on clicked button, do nothing or pass selected data
- (void)didEndAddItemSheet:(NSWindow *)sheet returnCode:(int)returnCode contextInfo (void *)contextInfo {
if (returnCode == 0) {
// do nothing, this is the effect of clicking on Cancel
}
if (returnCode == 1) {
NSString *theName = [[[itemsPickerController selectedObjects] valueForKey:#"itemName"] objectAtIndex:0];
// above, we get the value from the itemName attribute sleected in the Items list
NSLog (#"%#", theName);
// the variable is displayed in the console, so it was correctly selected
[self addNewItemWithName:theName];
}
}
// we use the passed data to create new object (row) in the CollectedItems entity
- (id)addNewItemWithName:(NSString *)theName {
NSEntityDescription *newContent = [NSEntityDescription insertNewObjectForEntityForName:#"CollectedItems" inManagedObjectContext:[self managedObjectContext]];
[newContent setValue:theName forKey:#"collectedItemName"];
// above, we insert a new row in CollectedItems, assigning the value theName to the attribute collectedItemName
return nil;
}
You need to add the relationship between your CollectedItems object you created and its parent the Collectors object. Collectors will have a core data utility method on it (if you have generated the core data managed object classes). It will be called something like addCollectedItemsObject.
Reloading the tableViews should then update with the correct data - as it now knows the relationship.
Even better would be to use NSFetchedResultsController to control the data in your tables so that when you update your data model, the tables will automatically reflect the changes.
And for the record, for those it might help, here is the final working code:
Ensuring the method was generated in the corresponding Collectors class file for (core data managed object classes).
// header file
#class Collecteditems;
#interface Collectors : NSManagedObject { }
#property (nonatomic, retain) NSSet* content;
#end
#interface Collectors (CoreDataGeneratedAccessors)
- (void)addContentObject:(Collecteditems *)value;
#end
// implementation file
#import "Collectors.h"
#import "Collecteditems.h"
#implementation Collectors
#dynamic content;
- (void)addContentObject:(Collecteditems *)value {
NSSet *s = [NSSet setWithObject:value];
[self willChangeValueForKey:#"collecteditems" withSetMutation:NSKeyValueUnionSetMutation usingObjects:s];
[[self primitiveValueForKey:#"collecteditems"] addObject:value];
[self didChangeValueForKey:#"collecteditems" withSetMutation:NSKeyValueMinusSetMutation usingObjects:s];
}
Then adding the addObject method on the NSArrayController controlling the collected items table (see id in original post for reference).
- (id)addNewItemWithName:(NSString *)theName {
NSEntityDescription *newContent = [NSEntityDescription insertNewObjectForEntityForName:#"CollectedItems" inManagedObjectContext:[self managedObjectContext]];
[newContent setValue:theName forKey:#"collectedItemName"];
[collectedItemsController addObject:newContent]; // line added
return nil;
}

Passing array of custom objects to NSCell in NSMatrix programmatically

I have an NSArray of custom NSObjects. Each object has some properties and an image that I would like to display in a grid view. NSMatrix appears to be a good solution to my problem, but I am having issues getting the content of the objects to display.
Couple of things to note.
I am not using core data
I am trying to do this programmatically
I have considered using NSCollectionView but NSMatrix appears to be a better solution in this case
All the cells follow the same display format as each other - i.e. I'm not wanting to pass different cells different types of objects, just a different instance of the object
Assume I have an NSView (matrixContainerView) in a window. The controller file has an IBOutlet to matrixContainerView. In my controller I have the following in my awakeFromNib:
NSMatrix* matrix = [[NSMatrix alloc]
initWithFrame:[matrixContainerView bounds]
mode:NSRadioModeMatrix
cellClass:[MyCustomCell class]
numberOfRows:5
numberOfColumns:5];
[matrix setCellSize:NSMakeSize(116, 96)];
[matrix setNeedsDisplay:YES];
[matrixContainerView addSubview:[matrix autorelease]];
[matrixContainerView setNeedsDisplay:YES];
The class MyCustomCell header looks like the following:
#interface MyCustomCell : NSCell {
MyModel * theObject;
}
-(MyModel *)theObject;
-(void)setTheObject:(MyModel *)newValue;
And the implementation file as follows (drawing simplified):
#implementation MyCustomCell
-(void)drawInteriorWithFrame:(NSRect)theFrame inView:(NSView *)theView {
...drawing code using MyModel e.g. [MyModel isValid] etc...
}
-(MyModel *)theObject {
return theObject;
}
-(void)setTheObject:(MyModel *)newValue {
[theObject autorelease];
theObject = [newValue retain];
}
#end
After some initialization and population of the array containing MyModel objects in the controller, I want to populate the NSMatrix with instances of the objects.
How do I do this?
I have tried adding just two objects from the array as follows (just as a test):
MyCustomCell * cellOne = (MyCustomCell *)[matrix cellAtRow:0 column:0];
[cell setTheObject:[myArrayOfObjects objectAtIndex:0]];
MyCustomCell * cellTwo = (MyCustomCell *)[matrix cellAtRow:0 column:1];
[cellTwo setTheObject:[myArrayOfObjects objectAtIndex:1]];
But this just creates the first object image. If the above had worked, it would been a straightforward task of enumerating through the array and adding the objects.
How do I go about adding the cells and passing the appropriate objects to those cells in order that they can be displayed correctly?
The Apple docs are sparse to say the least on NSMatrix as far as the programming guide goes. The information in there is very useful to me, but only after I have added the objects and got them displaying!
Update
If I do not add the two objects (as per my example above) the output is no different, i.e. a single representation of my custom cell is drawn to screen. This tells me that the single representation I see is being done at the initialization of the matrix and in fact I wasn't drawing anything to column 0 row 0 when in fact I thought I was. Which leaves me now more confused.
Might be that the matrix actually has the two cells but its frame is too small to display them?
After adding the cells try calling [matrix sizeToCells]