Custom selection style for view based "Source List" NSOutlineView

2.3k views Asked by At

I'm using a view based NSOutlineView that has it's selectionHighlightStyle set to NSTableViewSelectionHighlightStyleSourceList.

I want to overwrite the selection style (background) for certain rows and draw a different color/gradient.

What I tried so far is creating a custom NSTableRowView and returning it via outlineView:rowViewForItem:. I verified that my custom row views are created and returned by the outline view delegate. However, none of the methods I'm overwriting in the custom row view are being called.

I tried to overwrite drawBackgroundInRect:, drawSelectionInRect:, drawSeparatorInRect: and even drawRect:. None of those are called, ever.

I'm suspecting the outline view to be doing some custom "magic" when it's set to the source list style, but I've not found anything in the documentation that indicates that a custom NSTableRowView wouldn't be honored at all in this case.

3

There are 3 answers

6
Oskar On BEST ANSWER

Are you using Yosemite? From Apple's document Adopting Advanced Features of the new UI in Yosemite

When selectionHighlightStyle == NSTableViewSelectionHighlightStyleSourceList • Selection is now a special blue material that does behind window blending - The material size and drawing can not be customized

If you set it to NSTableViewSelectionHighlightStyleRegular and override the drawRect, it should work.

1
Denys Stas On

AppKit adds separate NSVisualEffectView with custom material to row view for drawing background when using NSTableViewSelectionHighlightStyleSourceList. I've come up with the following workaround which uses zero private APIs, but can break later if Apple implements some other way of highlighting rows.

@class CustomHighlightRowSelectionView;
@interface CustomHighlightRowView : NSTableRowView

@property (nonatomic, strong) CustomHighlightRowSelectionView *selectionView;

@end

@interface CustomHighlightRowSelectionView : NSView

@property (nonatomic, getter=isEmphasized) BOOL emphasized;
@property (nonatomic, getter=isSelected) BOOL selected;

@end


@implementation CustomHighlightRowView

- (CustomHighlightRowSelectionView *)selectionView
{
    if (!_selectionView)
    {
        _selectionView = [[CustomHighlightRowSelectionView alloc] initWithFrame:NSZeroRect];
    }

    return _selectionView;
}

- (void)setEmphasized:(BOOL)emphasized
{
    [super setEmphasized:emphasized];
    self.selectionView.emphasized = emphasized;
}

- (void)setSelected:(BOOL)selected
{
    [super setSelected:selected];
    self.selectionView.selected = selected;
}

- (void)addSubview:(NSView *)aView positioned:(NSWindowOrderingMode)place relativeTo:(NSView *)otherView
{
    if (![aView isKindOfClass:[NSVisualEffectView class]])
    {
        [super addSubview:aView positioned:place relativeTo:otherView];
    }
    else
    {
        if (!self.selectionView.superview)
        {
            [super addSubview:self.selectionView positioned:place relativeTo:otherView];
            self.selectionView.frame = self.bounds;
        }
    }
}

- (void)setFrame:(NSRect)frame
{
    [super setFrame:frame];

    self.selectionView.frame = self.bounds;
}

- (void)setBounds:(NSRect)bounds
{
    [super setBounds:bounds];

    self.selectionView.frame = self.bounds;
}

@end

@implementation CustomHighlightRowSelectionView

- (void)setEmphasized:(BOOL)emphasized
{
    _emphasized = emphasized;
    [self setNeedsDisplay:YES];
}

- (void)setSelected:(BOOL)selected
{
    _selected = selected;
    [self setNeedsDisplay:YES];
}

- (void)drawRect:(NSRect)dirtyRect
{
    if (!self.selected)
    {
        return;
    }

    NSColor *fillColor = self.emphasized ? [NSColor alternateSelectedControlColor] : [NSColor secondarySelectedControlColor];
    [fillColor setFill];
    NSRectFill(dirtyRect);
}

@end
3
Toby On

You'll need to overwrite -selectionHighlightStyle in your NSTableRowView subclass:

- (NSTableViewSelectionHighlightStyle)selectionHighlightStyle
{
    return NSTableViewSelectionHighlightStyleRegular;
}

That way, the table view can be used in source list style but with a customized row selection. I wanted to have the source list under Yosemite in my project but with the user-selected color from the System Preferences.

Edit: I just noticed doing it this way causes text fields and image views inside the cell view to have an artifact like border looking very odd and ugly.