1

How can I customise the groupItem-look in a view-based NSOutlineView? I would like to get rid of the divider border, change the background color and make the disclosure triangle dark. The background of the disclosure triangle should be the same color as the groupItem-view background.

I couldn't find any relevant info thru almighty Google.

Melodius
  • 2,505
  • 3
  • 22
  • 37

2 Answers2

3

The following NSOutlineView*Keys are used by the View Based NSOutlineView to create the "disclosure button" used to collapse and expand items.

APPKIT_EXTERN NSString * const NSOutlineViewDisclosureButtonKey NS_AVAILABLE_MAC(10_9); // The normal triangle disclosure button
APPKIT_EXTERN NSString * const NSOutlineViewShowHideButtonKey NS_AVAILABLE_MAC(10_9); // The show/hide button used in "Source Lists"

The NSOutlineView creates these buttons by calling [self makeViewWithIdentifier:owner:] passing in the key as the identifier and the delegate as the owner. Custom NSButtons (or subclasses thereof) can be provided for NSOutlineView to use in the following two ways:

  1. makeViewWithIdentifier:owner: can be overridden, and if the identifier is (for instance) NSOutlineViewDisclosureButtonKey, a custom NSButton can be configured and returned. Be sure to set the button.identifier to be NSOutlineViewDisclosureButtonKey.

  2. At design time, a button can be added to the outlineview which has this identifier, and it will be unarchived and used as needed.

    When a custom button is used, it is important to properly set up the target/action to do something (probably expand or collapse the rowForView: that the sender is located in). Or, one can call super to get the default button, and copy its target/action to get the normal default behavior.

    NOTE: These keys are backwards compatible to 10.7, however, the symbol is not exported prior to 10.9 and the regular string value must be used (i.e.: @"NSOutlineViewDisclosureButtonKey").

If you want to change also the position, than subclass your NSTableRowView and overwrite layout method

- (void)layout {

    [super layout];

    for (NSView * v in self.subviews) {
        if ([v.identifier isEqual:NSOutlineViewDisclosureButtonKey]) {
            v.frame = NSMakeRect(self.bounds.size.width - 44, 0, 44, self.bounds.size.height);
            v.hidden = NO;
            break;
        }
    }

}

and the code for the overwritten NSOutlineView

- (NSView *)makeViewWithIdentifier:(NSString *)identifier owner:(id)owner {

    NSView * v = [super makeViewWithIdentifier:identifier owner:owner];

    if ([identifier isEqual:NSOutlineViewDisclosureButtonKey] && ([v isKindOfClass:[NSButton class]])) {

        MenuDisclosureButton * b = [[MenuDisclosureButton alloc] initWithFrame:NSMakeRect(0, 0, 44, 44)];
        b.target = [(NSButton *)v target];
        b.action = [(NSButton *)v action];
        b.identifier = NSOutlineViewDisclosureButtonKey;
        v = b;

    }

    return v;
}
Peter Lapisu
  • 19,915
  • 16
  • 123
  • 179
  • This is very good, but only covers the disclosure button - not the actual group-row appearance. I can't find where I can subclass/replace the NSTableRowView (haven't found it either in the .xib hierarchy of NSOutlineView nor in the headers). Can't even understand its role in the play, relayed to the NSTableCellView which I can and do customize. Could you shed more light on this, or at least direct to documentation that explains how this works? – Motti Shneor Aug 18 '21 at 16:39
0

To customize (or hide) triangle button just override NSOutlineView class. The interest method is - (id)makeViewWithIdentifier:(NSString *)identifier owner:(id)owner

// your NSOutlineView child class

- (id)makeViewWithIdentifier:(NSString *)identifier owner:(id)owner
{
   id view = [super makeViewWithIdentifier:identifier owner:owner];
   if ([identifier isEqualToString:@"NSOutlineViewDisclosureButtonKey"])
     {
      NSButton *triangleButton = (NSButton *)view;
      NSImage *image = [[NSImage alloc] init]; // you could set another images
      [triangleButton setImage:image];
      [triangleButton setAlternateImage:image];
     }
   return view;
}

Triangle buttons must be hidden since now. But there still the indentation problem with your expandable items.

Open Interface Builder and select your outline view instance. Open Attributes inspector, set Indentation property to zero.

Update

In the group item view there's an empty space behind the triangle. Just to remove it set the indentation property to zero as I said above.

To set your custom separators just remove the NSOutlineView's ones and draw it yourself (in the drawRect method of your "cell" class), if you want to customize disclosure buttons, implement your own in your NSView-"cell" subclass.

Daniyar
  • 2,975
  • 2
  • 26
  • 39
  • But I do want to use indentation in my outline view. What about the borders and background color? – Melodius Feb 25 '15 at 13:08
  • @SeppoSilaste create custom NSView-subclass, set background and borders (via `drawRect`), use it for your View-based outline view. – Daniyar Feb 25 '15 at 13:15
  • setting a custom image for the disclosure button works fine, but changing the background to match other elements refuses to work. I've tried setting the view of the cell to a custom class with wantsLayer = true and then setting the backgroundColor for it: this will still leave the background for the disclosure button to some system default color. I've also tried doing the same with a subclass of NSTableRowView and returning that from the delegate method, but this only shows a border if I set it---the backgroundColor does not show anywhere. Any other suggestions? – Melodius Feb 27 '15 at 06:20
  • @SeppoSilaste did you try to use `drawRect` method to fill the view? http://stackoverflow.com/questions/2962790/best-way-to-change-the-background-color-for-an-nsview – Daniyar Feb 27 '15 at 07:07
  • Do you mean the view for the outline view itself or the view for a "cell" in the outlineview – Melodius Feb 27 '15 at 07:41
  • I mean a "cell"-view for view-based outline view. – Daniyar Feb 27 '15 at 07:47
  • I would think that the size of the view is not dependent on whether you use backgroundColor on a layer or drawRect. Or? I would like the whole outline view to be a flat light grey color (or any other color of my choosing). Now the background of the disclosure button never changes to match the other elements. And I also can't get rid of the grid separators in the group item views. It seems that these are drawn by the outline view itself. – Melodius Feb 27 '15 at 07:49
  • 1
    If I set indentation to 0 the disclosure triangle doesn't show as there is no space for it. Are you suggesting that i move the discosure button to my "cell" view and implement its functionality myself? – Melodius Feb 27 '15 at 09:24
  • If I override drawRect in "Cell" view for the grouped item and fill dirtyRect with a green, it will not draw under the disclosure button (in the indentation space). So either the width of the "Cell" view is smaller than the width of the outline view, or there is another view drawing on top of it under the disclosure triangle. – Melodius Feb 28 '15 at 11:31
  • @Melodius Yes, it won't draw under the disclosure button, that's why I told you to remove the indentation space at all. – Daniyar Mar 02 '15 at 06:36
  • How do you then handle the disclosure button? Do you add it manually to your "group item cell"? If yes, how do you then handle the indentation of sublevels? Thanx for your input! – Melodius Mar 02 '15 at 09:30
  • @Melodius You may ignore indentation. If it's important, declare cell's method `setIndentationLevel:(NSUInteger)level` which will change the leading layout constraint according to the `level` value. – Daniyar Mar 02 '15 at 09:55
  • 1
    And what about the disclosure button: do you add your own button to the "group item cell"? – Melodius Mar 02 '15 at 11:41
  • 1
    @Melodius yes I do. And implement custom actions selectors. – Daniyar Mar 02 '15 at 13:36