View based NSTableView selection highlighting
Asked Answered
P

4

6

I am trying to port an iOS application onto the Mac and I came across a couple of issues during the transition. One of them is the customization of NSTableView. What exactly is the difference between NSCell, NSTableRowView and custom NSView based NSTableview ? I initially started out with a view based NSTableView, but I soon noticed that I would have to handle the selection myself. I could not pull that off, so I went on to use NSTableRowView, which, strangely, does not call the initialiser of my custom NSTableRowView.

I basically just want a custom table view cell with custom contents, which is selectable. What is the best way to do it ?

On iOS, I would just subclass UITableViewCell and set its selectedView property. On Mac this seems to be more complicated than that.

Pagurian answered 17/1, 2014 at 16:51 Comment(0)
P
18

I have actually just found (in the sidebar) this question, which advises to subclass NSTableRowView. I had already done that before, but it did not work. I have tried it again and quite surprisingly it works now...

Handling custom selection style in view based NSTableView

However, this answer is not very informative, so I will try to cover what I have done in order to make this work.

First of all, I implemented the following NSTableView delegate method and return nil!:

- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row{

    return nil;

}

In order to use a view based (I guess NSTableViewRow is regarded a view based table as well...) table you HAVE TO implement this method. I am not quite sure what I might have done wrong, but without this method, my cells are not displayed!

Make sure to not let the NSTableView handle any selection by setting this property:

yourNSTableView.selectionHighlightStyle = NSTableViewSelectionHighlightStyleNone;

Okay, so now we want to set up our cells with the following delegate method:

-(NSTableRowView *)tableView:(NSTableView *)tableView rowViewForRow:(NSInteger)row{

    static NSString *cellID = @"cell_identifier";

    //use this if you want to reuse the cells
    CustomTableRowView *result = [tableView makeViewWithIdentifier:cellID owner:self];

    if (result == nil) {

        result = [[CustomTableRowView alloc] initWithFrame:NSMakeRect(0, 0, self.frame.size.width, 80)];
        result.identifier = cellID;

    }

    result.data = [tableData objectAtIndex:row];

    // Return the result
    return result;

}

Okay so now subclass NSTableRowView and implement/override the following two methods:

First we have to override setSelected: in order to make the cell redraw its background when it is selected. So here it is:

-(void)setSelected:(BOOL)selected{

    [super setSelected:selected];
    [self setNeedsDisplay:YES];

}

As mentioned earlier, we call setNeedsDisplay: in order for the cell to redraw its background.

Finally, the drawing code. Override the method drawBackgroundInRect: like this:

-(void)drawBackgroundInRect:(NSRect)dirtyRect{
    if (!self.selected) {
        [[NSColor clearColor] set];
    } else {
        [someColor set];
    }
    NSRectFill(dirtyRect);
}
Pagurian answered 17/1, 2014 at 16:57 Comment(2)
I found I didn't need the first bit, but the rest helped so +1. Thanks.Belamy
In case it helps anyone else, I was trying to do this using Swift on 10.9, and getting the CGContextRef is a bit different. See this answer for help on that: https://mcmap.net/q/376361/-how-to-use-implicitly-unwrapped-optionalsMerete
R
4

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];
Rosemaryrosemond answered 4/12, 2014 at 7:50 Comment(2)
if click other application than jump back to this application, the selected row will become blue again.Deloisedelong
Setting NSTableViewSelectionHighlightStyleNone seems to have gotten rid of the blue selection completely, even without setEmphasized! (macOS 10.15 Catalina)Shelashelagh
E
2

the_critic's solution in Swift: (tested with XCode 7 beta3, Swift 2.0)

class CustomTableRowView: NSTableRowView {

    override var selected: Bool {
        willSet(newValue) {
            super.selected = newValue;
            needsDisplay = true
        }
    }

    override func drawBackgroundInRect(dirtyRect: NSRect) {
        let context: CGContextRef = NSGraphicsContext.currentContext()!.CGContext

        if !self.selected {
            CGContextSetFillColorWithColor(context, NSColor.clearColor().CGColor)
        } else {
            CGContextSetFillColorWithColor(context, NSColor.redColor().CGColor)
        }

        CGContextFillRect(context, dirtyRect)
    }

}

ViewController:

class ViewController: NSViewController, NSTableViewDelegate, NSTableViewDataSource {

    // [...]

    func tableView(tableView: NSTableView, rowViewForRow row: Int) -> NSTableRowView? {
        guard let rowView = tableView.makeViewWithIdentifier("RowView", owner: nil) as! CustomTableRowView? else {
            let rowView = CustomTableRowView()
            rowView.identifier = "RowView"
            return rowView
        }

        return rowView
    }

}
Eatage answered 21/7, 2015 at 6:46 Comment(0)
S
0

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

Shelashelagh answered 22/1, 2021 at 21:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.