Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NSTableView has unwanted space above first row

I have an NSTableView whose first row is pushed down 10 pt from the top. Headers are turned off, there are no group rows, cell spacing is 0, and the enclosing scroll view's content inset is 0.


UPDATED

Here's a sample project that demonstrates the issue.


Here's a grab of the view hierarchy debugger:

enter image description here

Here's the vertical constraints of the first row's NSTableRowView while paused in the view hierarchy debugger:

enter image description here

I tried implementing the delegate's tableView(_:didAdd:forRow:) and inspecting the constraints, first with constraintsAffectingLayout(for:):

[<NSLayoutConstraint:0x60000390bd40 'NSTableRowView_Encapsulated_Layout_Height' NSTableRowView:0x7fdb12e26eb0.height == 24 priority:500   (active)>]

Then printing all the row view's constraints:

  - 0 : <NSLayoutConstraint:0x60000390bcf0 'NSTableRowView_Encapsulated_Layout_Width' NSTableRowView:0x7fdb12e26eb0.width == 329 priority:500   (active)>
  - 1 : <NSLayoutConstraint:0x60000390bd40 'NSTableRowView_Encapsulated_Layout_Height' NSTableRowView:0x7fdb12e26eb0.height == 24 priority:500   (active)>
  - 2 : <NSAutoresizingMaskLayoutConstraint:0x60000390bbb0 h=--& v=-&- InlineCell.minX == 16   (active, names: InlineCell:0x7fdb12e283a0, '|':NSTableRowView:0x7fdb12e26eb0 )>
  - 3 : <NSAutoresizingMaskLayoutConstraint:0x60000390bc00 h=--& v=-&- InlineCell.width == 297   (active, names: InlineCell:0x7fdb12e283a0 )>
  - 4 : <NSAutoresizingMaskLayoutConstraint:0x60000390bc50 h=--& v=-&- InlineCell.minY == 0   (active, names: InlineCell:0x7fdb12e283a0, '|':NSTableRowView:0x7fdb12e26eb0 )>
  - 5 : <NSAutoresizingMaskLayoutConstraint:0x60000390bca0 h=--& v=-&- V:[InlineCell]-(0)-|   (active, names: InlineCell:0x7fdb12e283a0, '|':NSTableRowView:0x7fdb12e26eb0 )>

The cell's minY constraint is set to 0, but the row is using an autoresizing mask. The table uses a simple diffable datasource:

NSTableViewDiffableDataSourceReference(tableView: table) { tableView, column, row, item in
    guard let cell = tableView.makeView(withIdentifier: inlineCellIdentifier, owner: self) as? NSTableCellView else { 
        preconditionFailure("Failed to create results cell") 
    }
    cell.textField?.textColor = self.themeAttributes.color
    cell.textField?.font = self.themeAttributes.font
    cell.textField?.stringValue = self.displayString(for: item)
    return cell
}

The only delegate method implemented is tableView(_:heightOfRow:). The table aligns itself with the lines of a sibling text view so it gets it row height from there:

func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat {
    guard let layoutManager = editor?.layoutManager else { 
        preconditionFailure("Missing layout manager in editor") 
    }
    return height(of: row, in: layoutManager)
}

This seems like it's probably obvious, but I don't see why the table is forcing its rows to be offset like this. I've found plenty of questions on this forum asking how to insert a gap at the top of the table, but not how to remove one. Any advice?

like image 787
smr Avatar asked Jul 30 '20 14:07

smr


1 Answers

It's not a bug, as stated in some comments, it's a new way how the NSTableView works in Big Sur (actually there's a bug, but elsewhere, see below). Free Pascal site contains nice overview of what's new.

New NSTableView properties

macOS Big Sur introduced new NSTableView properties:

  • style
  • effectiveStyle

style documentation:

The default value for this property is NSTableView.Style.automatic in macOS 11. Apps that link to previous macOS versions default to NSTableView.Style.fullWidth.

effectiveStyle documentation:

If the style property value is NSTableView.Style.automatic, then this property contains the resolved style.

.automatic documentation:

The system resolves the table view style in the following manner:

  • If the table view is in a sidebar split-view controller item, effectiveStyle resolves to NSTableView.Style.sourceList.
  • If the table’s scroll view has a border, effectiveStyle resolves to NSTableView.Style.fullWidth.
  • Otherwise effectiveStyle resolves to NSTableView.Style.inset. However, if the table needs extra space to fit its column cells, effectiveStyle resolves to NSTableView.Style.fullWidth.

Your table view has style set to .automatic in the Main.storyboard. Which means that the effective style resolves to .inset -> 10pt around content (based on the rules from the .automatic documentation).

Open the view debugger with a selected row. You can see the .inset style effect:

enter image description here

Use .fullWidth to remove 10pt insets.

You can also test the .automatic style behavior - try to add a border to the scroll view. Resolves to .fullWidth.

Bug & workaround

You probably tried to set the table view style to Full Width in the Interface Builder. It doesn't work. No matter what value you choose, it behaves like .automatic.

Add the following line to your code to workaround this issue:

tableView?.style = .fullWidth

Works as expected now:

enter image description here

Reported as FB8258910.

like image 132
zrzka Avatar answered Sep 28 '22 10:09

zrzka