Change NSTableView alternate row colors

Viewed 9077

I'm using the "Alternating Rows" option in Interface Builder to get alternating row colors on an NSTableView. Is there any way to change the colors of the alternating rows?

9 Answers

Swift 4+ version of AlternateRealist's answer:

public class AlternateBgColorTableView: NSTableView {
    var alternateBackgroundColor: NSColor? = .red

    override public func drawBackground(inClipRect clipRect: NSRect) {
        // If we didn't set the alternate color, fall back to the default behavior
        guard let alternateBackgroundColor = alternateBackgroundColor else {
            super.drawBackground(inClipRect: clipRect)

            return
        }

        // Fill in the background color
        backgroundColor.set()
        clipRect.fill()

        // Check if we should be drawing alternating colored rows
        if usesAlternatingRowBackgroundColors {
            // Set the alternating background color
            alternateBackgroundColor.set()

            // Go through all of the intersected rows and draw their rects
            var checkRect = bounds
            checkRect.origin.y = clipRect.origin.y
            checkRect.size.height = clipRect.height
            let rowsToDraw = rows(in: checkRect)
            var currentRow = rowsToDraw.location

            repeat {
                if currentRow % 2 != 0 {
                    // This is an alternate row
                    var rowRect = rect(ofRow: currentRow)
                    rowRect.origin.x = clipRect.origin.x
                    rowRect.size.width = clipRect.width
                    rowRect.fill()
                }

                currentRow += 1
            } while currentRow < rowsToDraw.location + rowsToDraw.length

            // Figure out the height of "off the table" rows
            var thisRowHeight = rowHeight

            if gridStyleMask.contains(.solidHorizontalGridLineMask) || gridStyleMask.contains(.dashedHorizontalGridLineMask) {
                thisRowHeight += 2 // Compensate for a grid
            }

            // Draw fake rows below the table's last row
            var virtualRowOrigin: CGFloat = 0
            var virtualRowNumber = numberOfRows

            if numberOfRows > 0 {
                let finalRect = rect(ofRow: numberOfRows - 1)
                virtualRowOrigin = finalRect.origin.y + finalRect.height
            }

            repeat {
                if virtualRowNumber % 2 != 0 {
                    // This is an alternate row
                    let virtualRowRect = NSRect(x: clipRect.origin.x, y: virtualRowOrigin, width: clipRect.width, height: thisRowHeight)
                    virtualRowRect.fill()
                }

                virtualRowNumber += 1
                virtualRowOrigin += thisRowHeight
            } while virtualRowOrigin < clipRect.origin.y + clipRect.size.height

            // Draw fake rows above the table's first row
            virtualRowOrigin = -1 * thisRowHeight
            virtualRowNumber = -1

            repeat {
                if abs(virtualRowNumber) % 2 != 0 {
                    // This is an alternate row
                    let virtualRowRect = NSRect(x: clipRect.origin.x, y: virtualRowOrigin, width: clipRect.width, height: thisRowHeight)
                    virtualRowRect.fill()
                }

                virtualRowNumber -= 1
                virtualRowOrigin -= thisRowHeight
            } while virtualRowOrigin + thisRowHeight > clipRect.origin.y
        }
    }
}

If you wish to set a custom color paired with the data in each row, I found this way of doing so, thanks to the posts here above, and this solution is also working to implement a custom alternate row color ( except for the empty rows ) :

create a subclass of NSTableView :

ColoredRowTableView.h

   @protocol ColoredRowTableViewDelegate <NSTableViewDelegate>
   -(NSColor *)colorForRow:(NSInteger)row;
   @end
   @interface ColoredRowTableView : NSTableView
   @end

in ColoredRowTableView.m

- (void)drawRow:(NSInteger)row clipRect:(NSRect)clipRect
{
    if (row == [self selectedRow])
    {
        [super drawRow:row clipRect:clipRect];
         return;
    }
    NSColor *color = [(id<ColoredRowTableViewDelegate>)[self delegate] colorForRow:row];
    [color setFill];
    NSRectFill([self rectOfRow:row]);
    [super drawRow:row clipRect:clipRect];
}

then in your table view delegate :

#import "ColoredRowTableView.h"
@interface MyTableViewDelegate : NSViewController <ColoredRowTableViewDelegate>
// instead of <NSTableViewDelegate>

implement the colorForRow: method in your delegate. If the goal is only to provide alternate row it can return a color based on the row number, if it is even or odd. Also don't forget to change the class of your table view to ColoredRowTableView in interface builder

Related