Change highlighting color in NSTableView in Cocoa?

10,351

Solution 1

I am using this, and so far works perfectly:

- (void)highlightSelectionInClipRect:(NSRect)theClipRect
{

        // this method is asking us to draw the hightlights for 
        // all of the selected rows that are visible inside theClipRect

        // 1. get the range of row indexes that are currently visible
        // 2. get a list of selected rows
        // 3. iterate over the visible rows and if their index is selected
        // 4. draw our custom highlight in the rect of that row.

    NSRange         aVisibleRowIndexes = [self rowsInRect:theClipRect];
    NSIndexSet *    aSelectedRowIndexes = [self selectedRowIndexes];
    int             aRow = aVisibleRowIndexes.location;
    int             anEndRow = aRow + aVisibleRowIndexes.length;
    NSGradient *    gradient;
    NSColor *       pathColor;

        // if the view is focused, use highlight color, otherwise use the out-of-focus highlight color
    if (self == [[self window] firstResponder] && [[self window] isMainWindow] && [[self window] isKeyWindow])
    {
        gradient = [[[NSGradient alloc] initWithColorsAndLocations:
                     [NSColor colorWithDeviceRed:(float)62/255 green:(float)133/255 blue:(float)197/255 alpha:1.0], 0.0, 
                     [NSColor colorWithDeviceRed:(float)48/255 green:(float)95/255 blue:(float)152/255 alpha:1.0], 1.0, nil] retain]; //160 80

        pathColor = [[NSColor colorWithDeviceRed:(float)48/255 green:(float)95/255 blue:(float)152/255 alpha:1.0] retain];
    }
    else
    {
        gradient = [[[NSGradient alloc] initWithColorsAndLocations:
                     [NSColor colorWithDeviceRed:(float)190/255 green:(float)190/255 blue:(float)190/255 alpha:1.0], 0.0, 
                     [NSColor colorWithDeviceRed:(float)150/255 green:(float)150/255 blue:(float)150/255 alpha:1.0], 1.0, nil] retain];

        pathColor = [[NSColor colorWithDeviceRed:(float)150/255 green:(float)150/255 blue:(float)150/255 alpha:1.0] retain];
    }

        // draw highlight for the visible, selected rows
    for (aRow; aRow < anEndRow; aRow++)
    {
        if([aSelectedRowIndexes containsIndex:aRow])
        {
            NSRect aRowRect = NSInsetRect([self rectOfRow:aRow], 1, 4); //first is horizontal, second is vertical
            NSBezierPath * path = [NSBezierPath bezierPathWithRoundedRect:aRowRect xRadius:4.0 yRadius:4.0]; //6.0
                [path setLineWidth: 2];
                [pathColor set];
                [path stroke];

            [gradient drawInBezierPath:path angle:90];
        }
    }
}

Solution 2

I searched for hours for an answer on this as well, and although I found many fragments, none of them were complete. So here I submit another approach, which I am using with success.

1) Set your NSTableView selectionHighLightStyle to None

This is necessary to ensure that OSX does not simply apply it's own highlights over the top of yours, leaving you with a blue highlight.

You can do this either through IB or via code.

2) Subclass NSTableView, and override drawRow.

This will set the background color for your selected rows to primary (active window) and secondary (inactive).

- (void)drawRow:(NSInteger)row clipRect:(NSRect)clipRect
{
    NSColor* bgColor = Nil;

    if (self == [[self window] firstResponder] && [[self window] isMainWindow] && [[self window] isKeyWindow])
    {
        bgColor = [NSColor colorWithCalibratedWhite:0.300 alpha:1.000];
    }
    else
    {
        bgColor = [NSColor colorWithCalibratedWhite:0.800 alpha:1.000];
    }

    NSIndexSet* selectedRowIndexes = [self selectedRowIndexes];
    if ([selectedRowIndexes containsIndex:row])
    {
        [bgColor setFill];
        NSRectFill([self rectOfRow:row]);
    }
    [super drawRow:row clipRect:clipRect];
}

3) Implement an NSTableViewDelegate, attach it to your NSTableView, and implement willDisplayCell.

This will allow you to change the textColor of the rows on selection/deselection, in case your selection colors make the text hard to read.

- (void)tableView:(NSTableView *)aTableView willDisplayCell:(id)aCell forTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex
{
    // check if it is a textfield cell
    if ([aCell isKindOfClass:[NSTextFieldCell class]])
    {
        NSTextFieldCell* tCell = (NSTextFieldCell*)aCell;
        // check if it is selected
        if ([[aTableView selectedRowIndexes] containsIndex:rowIndex])
        {
            tCell.textColor = [NSColor whiteColor];
        }
        else
        {
            tCell.textColor = [NSColor blackColor];
        }
    }
}

And you are done.

Solution 3

- (void)drawInteriorWithFrame:(NSRect)frame inView:(NSView *)controlView
{

    if ([self isHighlighted])
    {
        NSRect bgFrame = frame;
        [[NSColor redColor] set];  
        NSRectFill(bgFrame);

    }
}

I use this code to deal with the height, the code is in my custom cell file

Share:
10,351
Artem
Author by

Artem

Updated on June 19, 2022

Comments

  • Artem
    Artem almost 2 years

    I am developing a Cocoa application and encountered a problem with highlighting. Standard highlighting color in MAC OS X applications is blue, but it doesn't suit my app, since because of design concepts, I need a green color for highlighting.

    I tried to subclass NSTableview and override method

    - (void)highlightSelectionInClipRect:(NSRect)clipRect
    

    but it didn't help.

    How to fix this problem?

  • Artem
    Artem over 12 years
    This method works perfectly, thank you very much!!! Initially I forgot to change Highlight to Source list in IB, now it works, cool.
  • Artem
    Artem about 12 years
    Works too, but doesn't it overlay what highlightSelectionInClipRect does?
  • adib
    adib over 11 years
    You shouldn't need a retain after an alloc-init sequence.
  • Bogdan
    Bogdan about 10 years
    It does work for me, I just tested ... ok, for ARC you need to remove the retains, that's not a big deal.
  • Duck
    Duck over 9 years
    It makes sense. Why would Apple add an option to NStableView, to change the color of the selected row like it did for iOS UITableViews if we can add all this code and have rage trying to figure it out. Man, Cocoa team needs to meed Cocoa Touch team urgently.
  • Micha Mazaheri
    Micha Mazaheri about 9 years
    That works only for cell-based table views, view-based need to do that in NSTableRowView.
  • ixany
    ixany over 7 years
    Would be great if you could provide a Swift solution, I wasn’t able to adapt your code.
  • tofutim
    tofutim over 6 years
    strangely, DrawRow never gets called for me... on Xamarin