NSOutlineView: remove disclosure triangle and indent

For future reference, the cleanest and simplest way to hide the disclosure triangle in expandable NSOutlineView items is by implementing the outlineView:shouldShowOutlineCellForItem: method of the NSOutlineViewDelegate protocol in your delegate:

-(BOOL)outlineView:(NSOutlineView *)outlineView shouldShowOutlineCellForItem:(id)item
{
    // replace this with your logic to determine whether the
    // disclosure triangle should be hidden for a particular item
    return [item hidesDisclosureTriangle];
}

Use PXSourceList. It's the style you're looking for with a very nice api.


I had to combine the two approaches above because outlineView:shouldShowOutlineCellForItem: alone does not remove the space reserved for the disclosure triangles (it does remove the triangles themselves).

Delegate:

- (BOOL)outlineView:(NSOutlineView *)outlineView shouldShowOutlineCellForItem:(id)item {
    return NO;
}

Subclass of NSOutlineView:

@implementation ExpandedOutlineView

#define kOutlineCellWidth 11
#define kOutlineMinLeftMargin 6

- (NSRect)frameOfCellAtColumn:(NSInteger)column row:(NSInteger)row {
    NSRect superFrame = [super frameOfCellAtColumn:column row:row];
    if (column == 0) {
        // expand by kOutlineCellWidth to the left to cancel the indent
        CGFloat adjustment = kOutlineCellWidth;

        // ...but be extra defensive because we have no clue what is going on here
        if (superFrame.origin.x - adjustment < kOutlineMinLeftMargin) {
            NSLog(@"%@ adjustment amount is incorrect: adjustment = %f, superFrame = %@, kOutlineMinLeftMargin = %f", NSStringFromClass([self class]), (float)adjustment, NSStringFromRect(superFrame), (float)kOutlineMinLeftMargin);
            adjustment = MAX(0, superFrame.origin.x - kOutlineMinLeftMargin);
        }

        return NSMakeRect(superFrame.origin.x - adjustment, superFrame.origin.y, superFrame.size.width + adjustment, superFrame.size.height);
    }
    return superFrame;
}

@end

Result:

Screenshot of NSOutlineView with no top-level indentation


You've run into the right person here. I've had to grapple with this just a week ago.

Removing the disclosure triangle: implement the frameOfOutlineCellAtRow: method in your NSOutlineView subclass and return NSZeroRect (only if you want to hide that particular row's triangle, of course.)

- (NSRect)frameOfOutlineCellAtRow:(NSInteger)row {
    return NSZeroRect;
}

Disable indenting: the outline view's standard layout reserves space at the far left to draw the triangles in, in case the item is expandable. But you can override that for individual items by specifying a different drawing frame. You also do that in your subclass, by responding to this message:

- (NSRect)frameOfCellAtColumn:(NSInteger)column row:(NSInteger)row {
    NSRect superFrame = [super frameOfCellAtColumn:column row:row];


    if ((column == 0) /* && isGroupRow */) {
        return NSMakeRect(0, superFrame.origin.y, [self bounds].size.width, superFrame.size.height);
    }
    return superFrame;
}