Alec Sloman
Alec Sloman

Reputation: 287

NSOutlineView Data Source Doesn't Allow Expanding

I have an NSOutlineView into which I am trying to load an Array of model objects as the data source.

In particular, the model objects, along with a bunch of top-level properties (like title, etc) have a BOOLEAN NSNumber that indicates whether they have children item (isRoute), along with an array property that actually contains said children items (which are, in fact, instances of the same model class).

@interface RoadModel : NSObject {

    NSString *_id;
    NSString *roadmapID;
    NSString *routeID;

    NSString *title;
    NSString *description;
    NSNumber *collapsed;
    NSNumber *isRoute;
    NSString *staff;
    NSNumber *start;

    NSArray *staffList;
    NSMutableArray *updates;
    NSMutableArray *uploads;
    NSMutableArray *children;
    NSNumber *childrenCount;

}

@property (nonatomic, copy) NSString *_id;
@property (nonatomic, copy) NSString *roadmapID;
@property (nonatomic, copy) NSString *routeID;

@property (nonatomic, copy) NSString *title;
@property (nonatomic, copy) NSString *description;
@property (nonatomic, copy) NSNumber *collapsed;
@property (nonatomic, copy) NSNumber *isRoute;
@property (nonatomic, copy) NSString *staff;
@property (nonatomic, copy) NSNumber *start;

@property (nonatomic, copy) NSArray *staffList;
@property (nonatomic, copy) NSMutableArray *updates;
@property (nonatomic, copy) NSMutableArray *uploads;
@property (nonatomic, copy) NSMutableArray *children;
@property (nonatomic, copy) NSNumber *childrenCount;

- (id)initWithJSONObject:(NSDictionary *)JSONObject;

This is the interface for the model object.

Now, in a particular view controller, I am instantiating an NSOutlineView and setting up it's data source with an array of these objects in the init method (root = roads). Here is the implementation of the dataSource.

@implementation RoadOutlineViewDataSource

- (id)initWithRoads:(NSArray *)roads {

    if (self = [super init])
        root = roads;

    return self;

}

- (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item {

    if (item == nil)
        return [root count];

    else {
        NSLog(@"ROUTE HAS CHILDREN");
        return [[item childrenCount] intValue];
    }
    return 0; 
}

- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
{
    if (item == nil)
        return YES;

    return [[item isRoute] boolValue];

}

- (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item {

    if (item == nil) {
        return [root objectAtIndex:index];
    } else {
        return [((RoadModel *)item).children objectAtIndex:index];
    }
    return nil;
}

- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item {

    return [item title];

}

@end

Now, where I'm getting tripped up is that the second method, wherein the data source tries to find out how many children each item has, only gets called once, for the root array. It does not even attempt to query each of the model items in the root, so what I get is a row for each model object, but the ones that are meant to be expandable are not.

As a point of interest, I implemented a delegate method which draws "expandable" rows differently, and that is working just fine. However, something in this implementation is causing the datasource to not ask each item if it has children, and thus I cannot expand those items that are meant to be expanded. I don't know why though! I have followed the Apple docs and a bunch of online resources but keep coming to this same problem. Any advice would be well appreciated. Thanks all.

EDIT:

Here's how I'm setting up the OutlineView

NSOutlineView *outlineView = [[NSOutlineView alloc] initWithFrame:self.frame];
    NSTableColumn *titleColumn = [[NSTableColumn alloc] initWithIdentifier:@"title"];
    [titleColumn setWidth:self.frame.size.width];
    [outlineView addTableColumn:titleColumn];
    [outlineView setRowHeight:22.0f];
    [outlineView setIndentationPerLevel:5.0];
    [outlineView setSelectionHighlightStyle:NSTableViewSelectionHighlightStyleSourceList];
    [outlineView setUsesAlternatingRowBackgroundColors:YES];
    [outlineView setGridStyleMask:NSTableViewDashedHorizontalGridLineMask];
    [scrollView setDocumentView:outlineView];
    RoadOutlineViewDelegate *delegate       = [[RoadOutlineViewDelegate alloc] init];
    RoadOutlineViewDataSource *dataSource   = [[[RoadOutlineViewDataSource alloc] initWithRoads:roads] retain];
    [outlineView setDataSource:dataSource];
    [outlineView setDelegate:delegate];
    [outlineView reloadData];

Upvotes: 0

Views: 1782

Answers (1)

Rob Keniger
Rob Keniger

Reputation: 46020

I think the problem is your -outlineView:isItemExpandable: delegate method. This is also called for your root object, so you need to return YES if the item passed to this method is nil.

Also, is your data source deliberately maintaining a weak reference to the roads array? If not, then "roads" will be deallocated because you don't retain it in your -init method.

Upvotes: 3

Related Questions