Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the right way to put some text vertically centered in a NSTableView's row?

My OS X app has a NSTableView with larger row height:

myTableView.rowSizeStyle = .large

I try to put some text in it but I found that the text I put in cannot be vertically centered.

Please see the code below. I have three columns and the first two use NSTextField and NSTextView to put some text. The third one is a check box button.

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

    if (tableColumn?.identifier == "Column1") {

        let field = NSTextField()
        field.stringValue = "someText"
        field.isBordered = false
        field.isEditable = false
        field.backgroundColor = NSColor.red
        return field
    } else if (tableColumn?.identifier == "Column2") {

        let field = NSTextView()
        field.string = "someText"
        field.backgroundColor = NSColor.green
        return field
    } else if (tableColumn?.identifier == "Column3") {

        let field = NSButton()
        field.setButtonType(NSSwitchButton)
        field.title = "check me"
        return field
    }

    return nil
}

My code is pretty much based on Apple's document: "Creating and Configuring an NSTextField Cell".

Here is the result:

enter image description here

As you can see, the texts in the first two columns are not vertically centered while the check box is vertically centered automatically.

When using func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? just like the Apple document has shown, what is the right way to put text so that it can be vertically centered?

EDIT: For some reason, I don't use Interface Builder. So the question sticks to coding the UI programatically.

like image 963
Joe Huang Avatar asked Jun 22 '17 11:06

Joe Huang


1 Answers

Simple solution:

Do not create the UI elements in code, do it in Interface Builder. The linked Apple document states:

The most likely situation is that you’ll have designed a cell in Interface Builder and will want to fetch it and then populate the values in that cell

  • For the NSTextView column delete the predefined table cell view and drag an NSTextView instance in the column. Resize the view and add missing AutoLayout constraints.
  • For the second vertically aligned column use the predefined table cell view and add constraints Align Center X and Align Center Y to the NSTextField object.
  • For the checkbox column delete the NSTextField instance in the predefined table cell view, drag a checkbox into the view and add also Align Center Y and the other missing constraints.

It's highly recommended to use Cocoa Bindings to connect the table view to the data source.

This screenshot below uses no code at all except the method

func numberOfRows(in tableView: NSTableView) -> Int {
    return 10
}

enter image description here

Edit:

Alternatively for Column1 – the NSTextField column – wrap the text field in an NSTableCellView instance and specify a frame, for example (a switch statement is swiftier and tableColumn can only be nil if the table view uses also sections / group rows):

   switch tableColumn!.identifier {

    case "Column1":
        let cellView = NSTableCellView()
        // the value for y: in the frame should be row.height / 2 - textField.height / 2
        let field = NSTextField(frame: NSRect(x:0.0, y:10.0, width:tableColumn!.width, height:17.0))
        field.stringValue = "someText"
        field.isBordered = false
        field.isEditable = false
        field.backgroundColor = NSColor.red
        cellView.addSubview(field)
        return cellView

    case "Column2":

        ...

    default: return nil

    }

Note:

Consider that the text in the NSTextView column can only be centered within the text view via NSAttributedString or you have to specify a frame for the enclosing scroll view.

PS: It's so so so much easier in Interface Builder – SCNR.

like image 150
vadian Avatar answered Sep 21 '22 08:09

vadian