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)
}
}