How to customize NSTableView header on macOS 10.12+?

3.6k views Asked by At

MacOS 10.12+, Xcode 8+, Swift 3:

I'd like to programmatically customize the font and drawing of an NSTableView header. I know there are older questions about this, but I couldn't find anything that works today.

For example, I tried to subclass NSTableHeaderCell to set a custom font:

class MyHeaderCell: NSTableHeaderCell {
    override func drawInterior(withFrame cellFrame: NSRect, in controlView: NSView) {
        NSLog("MyHeaderCell is drawing")
        font = NSFont.boldSystemFont(ofSize: 12)
        super.drawInterior(withFrame: cellFrame, in: controlView)
    }
}

And then use that subclass in my table view:

tableColumn.headerCell = MyHeaderCell()

I see the message "MyHeaderCell is drawing" in the console, but the font of the table header doesn't change.

2

There are 2 answers

2
RFAustin On

It has been some time since anyone has addressed this question. I've encountered the same very frustrating problem using Swift 5 and Xcode 12. Here is what I have learned using an nstableview approach that does not require subclassing.

  1. at the beginning of the func tableView(_ myTable: NSTableView... add the following lines of code:

     tableColumn?.headerCell.drawsBackground = true
     tableColumn?.headerCell.backgroundColor = fill1Tint 
    

This code changes the headerCell background color (and fill1Tint is some NSColor).

Then add:

    let paragraphStyle: NSMutableParagraphStyle = NSMutableParagraphStyle()
    paragraphStyle.alignment = NSTextAlignment.center
  1. within each tableColumn.identifier block, add an attributed text string such as:

         let title: String = "Value"
         tableColumn?.headerCell.attributedStringValue = NSAttributedString(string: title, attributes: [
             NSAttributedString.Key.font: fontMedium,
             NSAttributedString.Key.foregroundColor: text1Tint,
             NSAttributedString.Key.paragraphStyle : paragraphStyle])
    

This code enables both a different background and centered, attributed text.

0
sam On

Thanks to comments from @HeinrichGiesen and @Willeke, I got it working. I'm posting it here in case it helps someone down the line. Note that my way of customizing the background color isn't that flexible. I'm really just tinting the default drawing. It's good enough for my purposes.

final class MyHeaderCell: NSTableHeaderCell {

    // Customize background tint for header cell
    override func draw(withFrame cellFrame: NSRect, in controlView: NSView) {
        super.draw(withFrame: cellFrame, in: controlView)
        NSColor(red: 0.9, green: 0.9, blue: 0.8, alpha: 0.2).set()
        NSRectFillUsingOperation(cellFrame, .sourceOver)
    }

    // Customize text style/positioning for header cell
    override func drawInterior(withFrame cellFrame: NSRect, in controlView: NSView) {
        attributedStringValue = NSAttributedString(string: stringValue, attributes: [
            NSFontAttributeName: NSFont.systemFont(ofSize: 11, weight: NSFontWeightSemibold),
            NSForegroundColorAttributeName: NSColor(white: 0.4, alpha: 1),
        ])
        let offsetFrame = NSOffsetRect(drawingRect(forBounds: cellFrame), 4, 0)
        super.drawInterior(withFrame: offsetFrame, in: controlView)
    }
}