swiftmacosnstableviewnstablecellview

How do I vertically align text in a custom table


I have a swift program that displays a custom table. Originally I incorrectly used a NSTextField for each cell and ran into a formatting issue. As can be seen in the picture below, the text in each cell is up against the top border. I was unable to figure out how to solve the problem so I posted my original code below. A response suggested that I change the NSTextField to a NSTableCellView. So I changed my code (new code is now below) to incorporate the NSTableCellView. Unfortunately, I ended up with the same formatting problem. I thought I could apply a constraint to the NSTableCellView relative to its parent but with no success (attempt commented out in delegate). It would be appreciated if someone could point me in the right direction to solve this issue. If I need to post more of my code, please let me know.

enter image description here

// Coordinators delegate
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {

    let dataCell = CustomTableCellView()
    switch tableColumn?.identifier.rawValue {
    case "fund":
        dataCell.Setup(rectWidth: 100.0, row: row, column: tableColumn!, numberOfRows: closingValues.count)
        dataCell.textField?.stringValue = closingValues[row].fundName
    case "date":
        dataCell.Setup(rectWidth: 120.0, row: row, column: tableColumn!, numberOfRows: closingValues.count)
        dataCell.textField?.stringValue = SQLDateFormatter.string(from: closingValues[row].timeStamp)
    default:
        dataCell.Setup(rectWidth: 100.0, row: row, column: tableColumn!, numberOfRows: closingValues.count)
        dataCell.textField?.stringValue = String(format: "$%.2f", closingValues[row].close)
    }
//            let constraints = [
//                dataCell.topAnchor.constraint(equalTo: ??parent??.topAnchor, constant: 5.0)
//            ]
//            NSLayoutConstraint.activate(constraints)
    return dataCell
}

// Custom NSTableCellView
class CustomTableCellView: NSTableCellView {
    
    override init(frame frameRect: NSRect) {
        super.init(frame: frameRect)
    }
    func Setup(rectWidth: CGFloat, row: Int, column: NSTableColumn, numberOfRows: Int) {
        self.autoresizingMask = .width
        let nsRectangle: NSRect = NSMakeRect(0, 0, rectWidth, 24)
        let customTextField: CustomTextField = CustomTextField(frame: nsRectangle)
        customTextField.SetTextAttributes(row: row, column: column, numberOfRows: numberOfRows)
        self.textField = customTextField
        self.addSubview(customTextField)
    }
    required init?(coder decoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}


Solution

  • After some more thought, I realized all I needed to do is get access to the base line offset of the cell text. So I made 2 changes to my CustomTextField class (the code for which is included below). 1) Commented out all text attributes from the SetTextAttributes function. 2) Added all the code above super.draw(dirtyRect) in the override func draw(_ dirtyRect: NSRect). By going to an attributed string value I got access to the base line offset. This allowed me to move the text down vertically. Problem solved.

    enter image description here

    class CustomTextField :NSTextField {
        
        var row: Int = 0
        var column: NSTableColumn = NSTableColumn()
        var numberOfRows: Int = 0
        
        override init(frame frameRect: NSRect) {
            super.init(frame: frameRect)
        }
        
        required init?(coder: NSCoder) {
            super.init(coder: coder)
        }
        
        func SetTextAttributes(row: Int, column: NSTableColumn, numberOfRows: Int)   {
    //        textColor = .blue
    //        backgroundColor = .white
    //        isBordered = false
    //        isEditable = false
    //        font = NSFont(name: "Arial", size: 16)!
    //        alignment = .center
            self.row = row
            self.column = column
            self.numberOfRows = numberOfRows
        }
        
        override func draw(_ dirtyRect: NSRect) {
            isBordered = false
            isEditable = false
            let paragraphStyle = NSMutableParagraphStyle()
            paragraphStyle.alignment = .center
            attributedStringValue = NSAttributedString(string: stringValue, attributes:
                                                        [NSAttributedString.Key.foregroundColor: NSColor.blue,
                                                         NSAttributedString.Key.backgroundColor: NSColor.white,
                                                         NSAttributedString.Key.font: NSFont(name: "Arial", size: 16)!,
                                                         NSAttributedString.Key.paragraphStyle: paragraphStyle,
                                                         NSAttributedString.Key.baselineOffset: -4.0
                                                        ])
            super.draw(dirtyRect)
            NSColor.lightGray.set()
            let customBorder = NSBezierPath()
            customBorder.move(to: NSMakePoint(0.0, dirtyRect.height))
            customBorder.line(to: NSMakePoint(0.0, 0.0))
            customBorder.line(to: NSMakePoint(dirtyRect.width, 0.0))
            if column.title == "Closing" {
                customBorder.line(to: NSMakePoint(dirtyRect.width, dirtyRect.height))
            }
            if row == numberOfRows - 1 {
                
                if column.title == "Fund" || column.title == "Date" {
                    customBorder.move(to: NSMakePoint(dirtyRect.width, dirtyRect.height))
                    customBorder.line(to: NSMakePoint(0.0, dirtyRect.height))
                }
                customBorder.line(to: NSMakePoint(0.0, dirtyRect.height))
            }
            customBorder.lineWidth = 2
            customBorder.stroke()
        }
    }