Question

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

Was it helpful?

Solution

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]];
   }
}

OTHER TIPS

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top