This is my first question here, and I will try to do it as clear as possible.

I want to draw a custom gradient on a selected row in a view-based NSTableView, while adding a subtle raised effect. For this, I need to use a darker color for the grid-lines that are before and after the selected row (see here for an example). I have overrode drawSeparatorInRect: method in NSTableRowView to draw the custom separator line for the selected row (using isSelected method as a flag), but I cannot do the same for the above/below one (since I draw the line at the bottom/top).

I have tried several ways to tell the closest row that it should draw a darker separator line with no success since the display step does not follow the same order (I checked it with NSLogs in the drawSeparatorInRect:, and it seems that when you scroll a little this order changes). So, sometimes (mostly after scrolling) the row doesn't know that it should use a darker color since it draws itself before the selected one (I think at this point, the selected-row is not aware yet that it's selected, otherwise I don't understand what is going on).

Some of the things I tried:

In the drawSeparatorInRect: method of the selected row, I have tried to access to the siblings views ([superview subviews]) and force the previous/next one to draw itself again.

From the NSTableView subclass, modify directly the closest row when the selectedIndexes change.

Drawing the line outside the selected row from within its drawSeparatorInRect: method as showing here.

Note that I did this having: a row view asking if the previous/next one is selected, a closestRowIsSelected flag or externally calling a method to "force" the dark color.

What I have now is that the selected row draws both top and bottom borders, so one of them is placed together to the previous/next row line... It's subtle but it still there.

Any help will be well received.

Thank you in advance.

! I didn't post any code since the problem is not there (it just calls [NSBezierPath fillRect:rect] with red color), I think... so I have nothing to show.

1 Answer
1

I've also tried this and noticed that drawSeparatorInRect: can really only draw its bottom separator line as the position of the top separator line (which is the same as the bottom separator line of the preceding row) is one pixel outside (above) the clipRect of the row.

However, I got it working by subclassing NSTableRowView and having drawSeparatorInRect: as follows:

This method will (only) draw its own bottom separator line. If it is the selected row, it will draw the line not with the default color, but highlighted, it will then also tell the preceding row to redraw its separator line, i.e. the same as the top separator line of the selected row.

In order to get this working, the row above the selected row needs to redraw its bottom separator line once the selection moves. I achieved this by having this method in the NSTableView delegate:

Actually the Apple documentation turned out to be right: The default bevaviour of NSTableRowView is to draw the separator line at the bottom of the row, not at the top. I've edited the code sample above accordingly.
–
TimJan 2 '12 at 23:41

Thank you so much, it certainly does what I was trying to do. However, whenever I select a row or scroll the tableview, the previous-to-selected row redraws itself multiple times, which produces an ultra-bold text and other weird effects. Did I miss something?
–
Francisco AdasmeJan 22 '12 at 17:50

I got it working perfectly with my old code. However, I moved the piece of code that forces the preceding cell view to draw itself, located at the end of drawSeparatorInRect: method, to a delegate method that gives me the new selected row (I used tableView:shouldSelectRow:). Again, thank you for your assistance.
–
Francisco AdasmeJan 27 '12 at 1:41

I would rather put the setNeedsDisplayInRect: call inside the outlineViewSelectionDidChange: delegate method.
–
Vincent TourraineFeb 25 '14 at 9:28