macoscocoaosx-lionnsoutlineviewdisclosure

How to customize disclosure cell in view-based NSOutlineView


I'm trying to customize the disclosure arrow appearance in my view-based NSOutlineView. I saw that it's recommended to use

- (void)outlineView:(NSOutlineView *)outlineView willDisplayOutlineCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item

delegate method to achieve it. The problem is that this method is not called for some reason. I have 2 custom cell views - one for item and second for header item. May be this method is not called for view-based outline views? May be something became broken in Lion?

Please shed some light.


Solution

  • This answer is written with OS X 10.7 in mind, for newer versions of OS X/macOS, refer to WetFish's answer

    That method does not get called because it is only relevant for cell based outline views.

    In a view based outline view, the disclosure triangle is a regular button in the row view of expandable rows. I don't know where it gets added, but it does, and NSView's didAddSubview: method handles exactly that situation of a view being added somewhere else.

    Hence, subclass NSTableRowView, and override didAddSubview:, like this:

    -(void)didAddSubview:(NSView *)subview
    {
        // As noted in the comments, don't forget to call super:
        [super didAddSubview:subview];
    
        if ( [subview isKindOfClass:[NSButton class]] ) {
            // This is (presumably) the button holding the 
            // outline triangle button.
            // We set our own images here.
            [(NSButton *)subview setImage:[NSImage imageNamed:@"disclosure-closed"]];
            [(NSButton *)subview setAlternateImage:[NSImage imageNamed:@"disclosure-open"]];
        }
    }
    

    Of course, your outline view's delegate will have to implement outlineView:rowViewForItem: to return the new row view.

    Despite the name, frameOfOutlineCellAtRow: of NSOutlineView still gets called for view based outline views, so for the positioning of your triangle, you might want to subclass the outline view and override that method, too.