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 row
Found a better way to do it here. That method overrides the highlightSelectionInClipRect: method in an NSTableView subclass so you can use any color you want for the alternating rows. It's not as hackish as using an NSColor category, and it only affects table views you choose.
I wanted a solution that worked just like the regular NSTableView
, including support for elastic scrolling and such, so I created an NSTableView
subclass that has an NSColor*
property called alternateBackgroundColor
, and then overrode the -drawBackgroundColorInClipRect:
method like so:
- (void) drawBackgroundInClipRect:(NSRect)clipRect {
if([self alternateBackgroundColor] == nil) {
// If we didn't set the alternate colour, fall back to the default behaviour
[super drawBackgroundInClipRect:clipRect];
} else {
// Fill in the background colour
[[self backgroundColor] set];
NSRectFill(clipRect);
// Check if we should be drawing alternating coloured rows
if([self alternateBackgroundColor] && [self usesAlternatingRowBackgroundColors]) {
// Set the alternating background colour
[[self alternateBackgroundColor] set];
// Go through all of the intersected rows and draw their rects
NSRect checkRect = [self bounds];
checkRect.origin.y = clipRect.origin.y;
checkRect.size.height = clipRect.size.height;
NSRange rowsToDraw = [self rowsInRect:checkRect];
NSUInteger curRow = rowsToDraw.location;
while(curRow < rowsToDraw.location + rowsToDraw.length) {
if(curRow % 2 != 0) {
// This is an alternate row
NSRect rowRect = [self rectOfRow:curRow];
rowRect.origin.x = clipRect.origin.x;
rowRect.size.width = clipRect.size.width;
NSRectFill(rowRect);
}
curRow++;
}
// Figure out the height of "off the table" rows
CGFloat rowHeight = [self rowHeight];
if( ([self gridStyleMask] & NSTableViewSolidHorizontalGridLineMask) == NSTableViewSolidHorizontalGridLineMask
|| ([self gridStyleMask] & NSTableViewDashedHorizontalGridLineMask) == NSTableViewDashedHorizontalGridLineMask) {
rowHeight += 2.0f; // Compensate for a grid
}
// Draw fake rows below the table's last row
CGFloat virtualRowOrigin = 0.0f;
NSInteger virtualRowNumber = [self numberOfRows];
if([self numberOfRows] > 0) {
NSRect finalRect = [self rectOfRow:[self numberOfRows]-1];
virtualRowOrigin = finalRect.origin.y + finalRect.size.height;
}
while(virtualRowOrigin < clipRect.origin.y + clipRect.size.height) {
if(virtualRowNumber % 2 != 0) {
// This is an alternate row
NSRect virtualRowRect = NSMakeRect(clipRect.origin.x,virtualRowOrigin,clipRect.size.width,rowHeight);
NSRectFill(virtualRowRect);
}
virtualRowNumber++;
virtualRowOrigin += rowHeight;
}
// Draw fake rows above the table's first row
virtualRowOrigin = -1 * rowHeight;
virtualRowNumber = -1;
while(virtualRowOrigin + rowHeight > clipRect.origin.y) {
if(abs(virtualRowNumber) % 2 != 0) {
// This is an alternate row
NSRect virtualRowRect = NSMakeRect(clipRect.origin.x,virtualRowOrigin,clipRect.size.width,rowHeight);
NSRectFill(virtualRowRect);
}
virtualRowNumber--;
virtualRowOrigin -= rowHeight;
}
}
}
}
Nate Thorn's answer worked perfectly for me.
Here it is, refactored for Swift:
import Foundation
import Cocoa
import AppKit
public class SubclassedTableView : NSTableView {
private func
alternateBackgroundColor() -> NSColor? {
return NSColor.redColor() // Return any color you like
}
public override func
drawBackgroundInClipRect(clipRect: NSRect) {
if alternateBackgroundColor() == nil {
// If we didn't set the alternate colour, fall back to the default behaviour
super.drawBackgroundInClipRect(clipRect)
} else {
// Fill in the background colour
self.backgroundColor.set()
NSRectFill(clipRect)
// Check if we should be drawing alternating coloured rows
if usesAlternatingRowBackgroundColors {
// Set the alternating background colour
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.size.height
let rowsToDraw = rowsInRect(checkRect)
var curRow = rowsToDraw.location
repeat {
if curRow % 2 != 0 {
// This is an alternate row
var rowRect = rectOfRow(curRow)
rowRect.origin.x = clipRect.origin.x
rowRect.size.width = clipRect.size.width
NSRectFill(rowRect)
}
curRow++
} while curRow < rowsToDraw.location + rowsToDraw.length
// Figure out the height of "off the table" rows
var thisRowHeight = rowHeight
if gridStyleMask.contains(NSTableViewGridLineStyle.SolidHorizontalGridLineMask)
|| gridStyleMask.contains(NSTableViewGridLineStyle.DashedHorizontalGridLineMask) {
thisRowHeight += 2.0 // Compensate for a grid
}
// Draw fake rows below the table's last row
var virtualRowOrigin = 0.0 as CGFloat
var virtualRowNumber = numberOfRows
if numberOfRows > 0 {
let finalRect = rectOfRow(numberOfRows-1)
virtualRowOrigin = finalRect.origin.y + finalRect.size.height
}
repeat {
if virtualRowNumber % 2 != 0 {
// This is an alternate row
let virtualRowRect = NSRect(x: clipRect.origin.x, y: virtualRowOrigin, width: clipRect.size.width, height: thisRowHeight)
NSRectFill(virtualRowRect)
}
virtualRowNumber++
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.size.width, height: thisRowHeight)
NSRectFill(virtualRowRect)
}
virtualRowNumber--
virtualRowOrigin -= thisRowHeight
} while virtualRowOrigin + thisRowHeight > clipRect.origin.y
}
}
}
}