On 03/12/2008, at 11:51 AM, Matthew Johnson wrote:

I am creating a custom view which I would like to bind to NSTreeController. I have tried observing both [treeController arrangedObjects] and [[treeController arrangedObjects] childNodes]. Both seem to only notify that a change has occurred and do not provide any details about the change (insertion, removal, etc). If this is the case, you would have to walk the entire tree every time a change happens in order to detect changes. This seems like a pretty bad idea. There must be a better way to do this.

Not as far as I know, there's a bit of manual work in implementing this. I'd love to hear if anyone has a better way to do it.

I am hoping somebody can share an example of how to implement the observation necessary to create a custom view that binds to NSTreeController. Does anybody have sample code? Is anyone able to provide a high level idea of how NSOutlineView and NSBrowser do this? Is it even possible to do this efficiently with public API?


You can definitely do it as I am doing it at present, although you can only do it in 10.5+ as NSTreeController is horribly broken in 10.4.

Here is some code, edited in mail:

@implementation NSTreeController (Additions)
//returns a flattened array of all the "real" objects in the tree.
//We use this to handle registration and deregistration of observers of the objects that are being managed
-(NSArray *) treeNodesAsArray:(NSArray*)nodes
{
        NSMutableArray* theObjectArray=[NSMutableArray array];
        for(NSTreeNode *node in nodes)
        {
                [theObjectArray addObject:[node representedObject]];
                if([[node childNodes] count])
                {
[theObjectArray addObjectsFromArray:[self treeNodesAsArray:[node childNodes]]];
                }
        }
        return theObjectArray;
}

-(NSArray*) representedObjects
{
        if([self childrenKeyPath]==nil)
                return nil;
        NSArray* arrangedObjects=[self arrangedObjects];
        if(arrangedObjects==nil)
                return nil;
        NSArray* childNodes=[[self arrangedObjects]childNodes];
        if(childNodes==nil)
                return nil;
        return [self treeNodesAsArray:childNodes];
}
@end

Then, in your controller:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
        if (context == MyObservationContext)
        {
                NSArray *items = [treeController representedObjects];
                
                //if there has been no change, don't do anything
                if([items isEqualToArray:self.oldItems])
                        return;
                
                //get the items that have been added to the array
                NSMutableArray *newItems = [items mutableCopy];
                [newItems removeObjectsInArray: self.oldItems];
                
                NSMutableArray *removedItems = [self.oldItems mutableCopy];
                [removedItems removeObjectsInArray:items];
                
                //stop observing the removed items
                [self stopObservingOldItemProperties:removedItems];
                
                //start observing the new items
                [self startObservingNewItemProperties:newItems];
                
//store the current array of items as the old array so we can compare it next time
                self.oldItems=items;
                
                //do whatever UI adjustments you need to do
    }


--
Rob Keniger



_______________________________________________

Cocoa-dev mailing list ([email protected])

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to [EMAIL PROTECTED]

Reply via email to