I have code that displays an NSTableView. I have the functionality I want but am having problems with the formatting. In the makeNSView function I can set the header cell text, background color and alignment. What I have been unable to determine is how to add what I would term padding to the header cell background color such that it is taller, i.e. extends further above and below the text. As a secondary issue, I would like to be able to control the border widths of the table cells i.e. the NSViews, such that they appear all the same. Right now, adjacent cell edges have double width borders and non adjacent cell edges have single width borders. Below are the displayed results and code.
struct BetterTableView: NSViewRepresentable { func makeCoordinator() -> Coordinator { Coordinator() } class Coordinator: NSObject, NSTableViewDelegate, NSTableViewDataSource { var dateSorting: String = "ASC" var closingSorting: String = "ASC" var closingValues: [ClosingValue] = [] override init() { super.init() self.closingValues = GetValues() } func tableView(_ tableView: NSTableView, mouseDownInHeaderOf tableColumn: NSTableColumn) { switch tableColumn.title { case "Date": if dateSorting == "ASC" { closingValues.sort { $0.date > $1.date } dateSorting = "DESC" } else { closingValues.sort { $0.date < $1.date } dateSorting = "ASC" } case "Closing": if closingSorting == "ASC" { closingValues.sort { $0.date > $1.date } closingSorting = "DESC" } else { closingValues.sort { $0.date < $1.date } closingSorting = "ASC" } default: print("default") } tableView.reloadData() tableView.scrollRowToVisible(0) } func numberOfRows(in tableView: NSTableView) -> Int { closingValues.count } func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? { var nsView = NSView() let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.alignment = .center let attributes1: [NSAttributedString.Key: Any] = [ .foregroundColor: NSColor.blue, .paragraphStyle: paragraphStyle, .font: NSFont(name: "Arial", size: 14.0) as Any ] var attributedString = NSAttributedString() let tempNSView = NSTextField() switch tableColumn { case tableView.tableColumns[0]: attributedString = NSAttributedString(string: closingValues[row].name, attributes: attributes1) case tableView.tableColumns[1]: attributedString = NSAttributedString(string: closingValues[row].date, attributes: attributes1) case tableView.tableColumns[2]: let closeAsString = String(format: "$%.2f", closingValues[row].close) attributedString = NSAttributedString(string: closeAsString, attributes: attributes1) default: print("problem in table view switch statement") } tempNSView.backgroundColor = NSColor.white tempNSView.isBordered = true tempNSView.isEditable = false tempNSView.attributedStringValue = attributedString nsView = tempNSView return nsView } } // end of coordinator class func makeNSView(context: Context) -> NSScrollView { let tableView = NSTableView() tableView.style = .plain tableView.delegate = context.coordinator tableView.dataSource = context.coordinator tableView.addTableColumn(NSTableColumn()) tableView.addTableColumn(NSTableColumn()) tableView.addTableColumn(NSTableColumn()) tableView.intercellSpacing = NSSize(width: 0.0, height: 0.0) tableView.headerView?.frame.size.height = 20.0 let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.alignment = .center let attributes: [NSAttributedString.Key: Any] = [ .foregroundColor: NSColor.black, .paragraphStyle: paragraphStyle, .font: NSFont.systemFont(ofSize: 18) ] let column0AttributedString = NSAttributedString(string: "Stock", attributes: attributes) let column0Header = tableView.tableColumns[0] column0Header.headerCell.drawsBackground = true column0Header.headerCell.backgroundColor = NSColor.systemMint column0Header.headerCell.alignment = .center column0Header.headerCell.attributedStringValue = column0AttributedString column0Header.sizeToFit() column0Header.minWidth = 90.0 column0Header.maxWidth = 90.0 let column1AttributedString = NSAttributedString(string: "Date", attributes: attributes) let column1Header = tableView.tableColumns[1] column1Header.headerCell.drawsBackground = true column1Header.headerCell.backgroundColor = NSColor.systemMint column1Header.headerCell.alignment = .center column1Header.headerCell.attributedStringValue = column1AttributedString column1Header.sizeToFit() column1Header.minWidth = 125.0 column1Header.maxWidth = 125.0 let column2AttributedString = NSAttributedString(string: "Closing", attributes: attributes) let column2Header = tableView.tableColumns[2] column2Header.headerCell.drawsBackground = true column2Header.headerCell.backgroundColor = NSColor.systemMint column2Header.headerCell.alignment = .center column2Header.headerCell.attributedStringValue = column2AttributedString column2Header.sizeToFit() column2Header.minWidth = 90.0 column2Header.maxWidth = 90.0 let scrollView = NSScrollView() scrollView.documentView = tableView return scrollView } func updateNSView(_ nsView: NSScrollView, context: Context) { let tableView = (nsView.documentView as! NSTableView) print("in update ns view") // work on this section } }
The solution is to create a subclass of NSTableHeaderCell. In the subclass you override several functions and insert customizations as desired in the drawInterior function. Below is the call to and subclass created.
// in func makeNSView let customHeaderCell0 = CustomHeaderCell() customHeaderCell0.stringValue = "Stock" let column0Header = tableView.tableColumns[0] column0Header.minWidth = 90.0 column0Header.headerCell = customHeaderCell0 // subclass final class CustomHeaderCell: NSTableHeaderCell { override init(textCell: String) { super.init(textCell: textCell) } required init(coder: NSCoder) { fatalError("init(coder:) not implemented") } override func draw(withFrame cellFrame: NSRect, in controlView: NSView) { self.drawInterior(withFrame: cellFrame, in: controlView) } override func drawInterior(withFrame cellFrame: NSRect, in controlView: NSView) { let rect = NSRect(x: cellFrame.origin.x, y: cellFrame.origin.y, width: cellFrame.size.width, height: cellFrame.size.height) NSColor.systemMint.set() NSBezierPath(rect: rect).fill() let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.alignment = .center let headerCellText = NSAttributedString(string: stringValue, attributes: [NSAttributedString.Key.foregroundColor: NSColor.black, NSAttributedString.Key.font: NSFont(name: "Arial", size: 18)!, NSAttributedString.Key.paragraphStyle: paragraphStyle, NSAttributedString.Key.baselineOffset: -6.0 ]) headerCellText.draw(in: cellFrame) } }