3

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.

lemonmojo
  • 733
  • 5
  • 20

3 Answers3

4

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
Denys Stas
  • 161
  • 7
1

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.

Oskar
  • 3,625
  • 2
  • 29
  • 37
  • Yes, I'm using Yosemite and although I have watched the WWDC session you're referencing, I seem to have missed "The material size and drawing can not be customized". Looks I'm out of luck if I want both, use the Source List style and customize selection drawing for several rows. – lemonmojo Nov 18 '14 at 18:20
  • Are you trying to remove the blue highlight? If so, you might be interested in this: http://stackoverflow.com/questions/26596621/how-to-get-the-source-lists-selection-highlight-to-use-the-dark-vibrancy-appeara/26990727#26990727 – Oskar Nov 19 '14 at 03:16
  • That's definitely useful and I have just implemented this in another part of my app. Awesome finding! However, in this particular case, I'd like to overwrite the default blue highlight to be a completely different color/gradient. – lemonmojo Nov 19 '14 at 12:41
  • `drawSelectionInRect:` is called again in El Capitan. – ylian Oct 22 '15 at 14:41
  • @ylian how do you make `drawSelectionInRect:` it called in El Capitan? – Harry Ng Jan 07 '16 at 10:01
  • For me drawSelectionInRect is not called in El Captain – Lubos Jan 28 '16 at 20:19
1

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.

Toby
  • 181
  • 1
  • 9
  • 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. Bottom line - the source list style's selection color cannot be adjusted. Instead, I've used the regular style and manually designed the outline view to look like a source list one. Note: If you're targeting Yosemite and you want the translucent background look that is turned on by default when using the source list, you'll need to embed the outline view in an NSVisualEffectView. – Toby Jan 05 '15 at 16:31
  • I'm aware of the possibility of using NSTableViewSelectionHighlightStyleRegular and "faking" the source list appearance but I'd rather have non-custom selection styles instead of implementing all the source list styling myself... – lemonmojo Jan 07 '15 at 12:19
  • I agree, native is mostly better and more elegant but in this case you might be out of luck, I'm afraid. Finding a way to modify / hack the outline view to have customized selection rows in source list mode will take considerably longer than modifying the view manually. – Toby Jan 12 '15 at 22:24