Change selection color on view-based NSTableView
Asked Answered
B

14

44

Standard highlighting color in OS X applications is blue.

Is it possible to change it to another color, e.g. gray?

Note that I am using the new view-based NSTableView available starting from OS X 10.7.

Battledore answered 27/2, 2012 at 11:0 Comment(0)
F
78

Since you're using the view based NSTableView, you can subclass NSTableRowView, feed it to the table delegate method - (NSTableRowView *)tableView:(NSTableView *)tableView rowViewForRow:(NSInteger)row;, then customize your selection in the row view class.

Here's an example:

- (void)drawSelectionInRect:(NSRect)dirtyRect {
    if (self.selectionHighlightStyle != NSTableViewSelectionHighlightStyleNone) {
        NSRect selectionRect = NSInsetRect(self.bounds, 2.5, 2.5);
        [[NSColor colorWithCalibratedWhite:.65 alpha:1.0] setStroke];
        [[NSColor colorWithCalibratedWhite:.82 alpha:1.0] setFill];
        NSBezierPath *selectionPath = [NSBezierPath bezierPathWithRoundedRect:selectionRect xRadius:6 yRadius:6];
        [selectionPath fill];
        [selectionPath stroke];
    }
}
Fabrikoid answered 7/3, 2012 at 1:28 Comment(6)
WWDC 2011 session 120 "View Based NSTableView Basic to Advanced" gives a similar example, and also describes how to implement different colours depending on whether the table is firstResponder / "active selection" / "emphasized" or not.Delrosario
It seems that if the table view is declared as a source list drawSelectionInRect: is not called at all on OS X 10.10 Yosemite. Only when a table is declared as a regular table will this happen on Yosemite.Battledore
developer.apple.com/library/mac/samplecode/TableViewPlayground/…Selfseeking
As far as I can tell, this also overrides the background color of the "blurred" state of the table (like when you select a row, then click into an NSTextField somewhere else in your UI). This background color remains in place but the text goes dark. This doesn't look good if you use a dark highlight color. Any idea how to let the table use its default gray for the "blurred" table state?Bulldog
@Dev's comment is correct and should be added to the accepted answer.Orthodox
View Based NSTableView Basic to AdvancedClearcut
A
24

Here is James Chen's solution in Swift 3. I've also added the delegate method.

class MyNSTableRowView: NSTableRowView {

    override func drawSelection(in dirtyRect: NSRect) {
        if self.selectionHighlightStyle != .none {
            let selectionRect = NSInsetRect(self.bounds, 2.5, 2.5)
            NSColor(calibratedWhite: 0.65, alpha: 1).setStroke()
            NSColor(calibratedWhite: 0.82, alpha: 1).setFill()
            let selectionPath = NSBezierPath.init(roundedRect: selectionRect, xRadius: 6, yRadius: 6)
            selectionPath.fill()
            selectionPath.stroke()
        }
    }
}

NSTableViewDelegate:

func tableView(_ tableView: NSTableView, rowViewForRow row: Int) -> NSTableRowView? {
    return MyNSTableRowView()
}
Allistir answered 30/9, 2016 at 15:19 Comment(0)
G
14

Use the following code in response to the NSTableViewDelegate protocol tableViewSelectionDidChange:

Get the NSTableRowView for the selected row and call the method setEmphasized on it. When setEmphasized is set to YES you get the blue highlight, when NO you get the gray highlight.

-(void)tableViewSelectionDidChange:(NSNotification *)aNotification {

     NSInteger selectedRow = [myTableView selectedRow];
     NSTableRowView *myRowView = [myTableView rowViewAtRow:selectedRow makeIfNecessary:NO];
     [myRowView setEmphasized:NO];
}
Groceries answered 25/2, 2013 at 21:34 Comment(4)
This gives a dancing effect of blue then gray... bad user experience.Chilopod
This solution is not work when we click on the row first time. from second click it will work.Bendy
if you use same code while SelectionIsChanging method, it works for the first click tooPhosphatase
if I minimize application and open it again it still show blue colorPhosphatase
C
6

Some modifications to Jean-Pierre answer

Use the following code in response to the NSTableViewDelegate protocol tableViewSelectionDidChange:

Get the NSTableRowView for the selected row and call the method setEmphasized on it. When setEmphasized is set to YES you get the blue highlight, when NO you get the gray highlight.

-(void)tableViewSelectionDidChange:(NSNotification *)aNotification {

 NSInteger selectedRow = [myTableView selectedRow];
 NSTableRowView *myRowView = [myTableView rowViewAtRow:selectedRow makeIfNecessary:NO];
[myRowView setSelectionHighlightStyle:NSTableViewSelectionHighlightStyleRegular];
[myRowView setEmphasized:NO];
}

And to avoid dancing effect of blue then gray set

[_tableView setSelectionHighlightStyle:NSTableViewSelectionHighlightStyleNone];
Clercq answered 4/12, 2014 at 7:54 Comment(1)
I works , but it I minimize application and open it again, it again it bluePhosphatase
A
3

When using Swift you can do this on 10.10 for view based Cells

Subclass the NSTableCellView and implement this:

//override to change background color on highlight
override var backgroundStyle:NSBackgroundStyle{
    //check value when the style was setted
    didSet{
        //if it is dark the cell is highlighted -> apply the app color to it
        if backgroundStyle == .Dark{
            self.layer!.backgroundColor = yourColor
        }
        //else go back to the standard color
        else{
            self.layer!.backgroundColor = NSColor.clearColor().CGColor
        }
    }
}

Note that the NSTableView highlight style must be set to Regular if it is on SourceList it will cause some strange clipping.

This is not the cleanest solution but it works good on yosemite

Appoint answered 11/2, 2015 at 16:44 Comment(2)
It doesn't work. 10.10; view based cell; nstablecellview subclass with your code (but i had to use ? instead of ! after 'layer', otherwise i get crash); regular highlight style.Imtiaz
This works except when you have space between your cells. The default highlight color shows behind the cell backgrounds. Screenshot: d.pr/i/QzMu1I (Xcode 8.3.3, Swift 3.1)Bulldog
J
3

I've mixed all methods described before and got code that exactly do what I want.

  • Selection not change color of textfields inside;
  • Rows remember selection and color of one;
  • Any strange outer borders and other leftovers appear.

    class AudioCellView: NSTableRowView {
    
        override func draw(_ dirtyRect: NSRect) {
            super.draw(dirtyRect)
            self.wantsLayer = true
            self.layer?.backgroundColor = NSColor.white.cgColor
        }
    
        override var isEmphasized: Bool {
            set {}
            get {
                return false
            }
        }
    
        override var selectionHighlightStyle: NSTableView.SelectionHighlightStyle {
            set {}
            get {
                return .regular
            }
        }
    
        override func drawSelection(in dirtyRect: NSRect) {
            if self.selectionHighlightStyle != .none {
                let selectionRect = NSInsetRect(self.bounds, 2.5, 2.5)
                NSColor(calibratedWhite: 0.85, alpha: 0.6).setFill()
                let selectionPath = NSBezierPath.init(rect: selectionRect)
                selectionPath.fill()
            }
        }
    }
    
Janettjanetta answered 7/8, 2017 at 14:3 Comment(0)
M
2

As already mentioned, set emphasized attribute to false, but do it in the custom NSTableRowView class to avoid side effects (like dancing color effect):

    override func drawRect(dirtyRect: NSRect) {
       super.drawRect(dirtyRect)
       self.emphasized = false

    }
Motorist answered 28/1, 2016 at 20:27 Comment(0)
S
1

It seems to me there is an option available to change this coz the documentation says three selection style and the default style in regular is blue, look at the image below.. you need to send it a message which I cant figure out as I have never developed apps for mac before.. hoping this helps...!

enter image description here

Singapore answered 27/2, 2012 at 11:53 Comment(3)
Hello, selectionHighlightStyle has the value NSTableViewSelectionHighlightStyleRegular, but I don't know how to tell it how to use the alternate or secondary selected color...Battledore
The blue color is shown when the tableview is in focus. If not in focus gray color is displayed. There is no simple method to change the selection color.Mini
That would make too much sense.Ne
P
1

Okay, So I do know that it already has an accepted answer, but for anyone like me working with an NSOutlineView and has .selectionHighlightStyle = .sourceList can use this code to make the selection grey. This method will not flicker when changing the selection and will also stay grey if the app is minimised.

NSTableView/NSOutlineView Delegate:

func outlineView(_ outlineView: NSOutlineView, rowViewForItem item: Any) -> NSTableRowView?
{
     let row : CustomRowView = CustomRowView.init()
     row.identifier = "row"

     return row
}

And then create a new CustomRowView.swift file with this:

class CustomRowView : NSTableRowView
{
    override var isEmphasized: Bool {
        get { return self.isEmphasized }
        set(isEmp) { self.isEmphasized = false }
    }
}

This will keep the selection grey at all times.

Paleontography answered 5/1, 2017 at 12:3 Comment(1)
This solution will obviously result in a stack overflow. You are setting the isEmphasized property in the setter of the isEmphasized property. Simply use the setter to set a private variable instead. With this minor change the solution works great.Donough
B
0

Could not find this in IB either. The answers above that tell you to set this in the tableview delegate tableViewSelectionDidChange: do work, but the disadvantage is that you (I) still see a brief blue flicker on selection when you select the row.

I do the setEmphasized:NO in the datasource: tableView:viewForTableColumn:row:

NSTableRowView *rowView = [tableView rowViewAtRow:row makeIfNecessary:YES];
[rowView setEmphasized:NO];

So this setting is applied before any selection is made, and the blue never appears. Subclassing seems over the top for this problem.

EDIT1: the blue selection returns if you've scrolled and clicked around in the table, or went to another Application, and returned, so my suggestion is to do the [rowView setEmphasized:NO]; procedure in both the delegate and the datasource. The brief blue flicker still happens now and then though. (macOS 10.15.7 Catalina)

EDIT2: View based NSTableView selection highlighting suggests to add:

tableView.selectionHighlightStyle = NSTableViewSelectionHighlightStyleNone;

That seems to get rid of the blue selection completely, even without fiddling with setEmphasized!

EDIT 3(final): You can set this in IB after all, it's the setting "Highlight", under "Style", above the checkbox "Alternating rows". Set to "None", et voila.

enter image description here

Brainchild answered 22/1, 2021 at 17:39 Comment(0)
P
-1

You have to subclass NSTableView, and rewrite the functions below in order to change the alternating colors.

  • (void) drawRow: (NSInteger) row clipRect: (NSRect) clipRect

  • (void) drawBackgroundInClipRect: (NSRect) clipRect ** This one to change the main and alternate color **

Use a for loop and insert this conditional (i % 2 == 0) to detect odd and even rows.

Pulsifer answered 25/10, 2012 at 2:52 Comment(1)
To clarify, this is not the correct answer for view-based NSTableviews which the question was asking about. You should only override the above for cell based.Hurlyburly
M
-3

This is Jean-Pierre's answer in Swift3:

func tableViewSelectionDidChange(_ notification: Notification)
    { 
        index = tableView.selectedRow
        let rowView = tableView.rowView(atRow: index, makeIfNecessary: false)
        rowView?.isEmphasized = false
...

It has the two limitations listed above -- first click doesn't work, second click does. And, there is a "dancing effect". I don't mind the first and actually like the second.

Monody answered 22/3, 2017 at 13:15 Comment(0)
A
-4
- (void)tableViewSelectionDidChange:(NSNotification *)notification
{
    [tblCategory enumerateAvailableRowViewsUsingBlock:^(NSTableRowView *rowView, NSInteger row){
        CustomMainCell *cellView = [rowView viewAtColumn:0];
        if(rowView.selected){
            cellView.txtFieldTitle.textColor=[NSColor colorWithCalibratedRed:245.0/255.0 green:110.0/255.0 blue:65.0/255.0 alpha:1.0];
        }else{
            cellView.txtFieldTitle.textColor=[NSColor whiteColor];
        }
    }];
}

[tblCategory setSelectionHighlightStyle:NSTableViewSelectionHighlightStyleNone];

Aircondition answered 2/1, 2014 at 8:9 Comment(0)
J
-4
  Use this Notification for NSTableView:

          - (void)tableViewSelectionDidChange:(NSNotification *)notification
            {

                 //You Logic stuff
             }
Jewbaiting answered 2/1, 2014 at 8:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.