When to call noteHeightOfRows when cell view changes size?

I have a tableview with a column which has an inner tableview. I want to change height of the outer tableview to match the height of the inner tableview.

outerTableView.reloadData()
let range = outerTableView.rows(in: summaryTableView.superview!.visibleRect)
outerTableView.noteHeightOfRows(withIndexesChanged: IndexSet(integersIn: range.lowerBound..<range.upperBound))

The above code will cause an exception:

WARNING: NSTableView detected a rowView was requested from inside of the -heightOfRow delegate method. That is not supported!
(
	0   CoreFoundation                      0x000000019093eccc __exceptionPreprocess + 176
	1   libobjc.A.dylib                     0x0000000190426788 objc_exception_throw + 60
	2   AppKit                              0x000000019420be98 -[NSTableRowData _availableRowViewWhileUpdatingAtRow:] + 0
	3   AppKit                              0x000000019425a470 -[NSTableView viewAtColumn:row:makeIfNecessary:] + 32

Replies

I did much search on the web and found no answer. The only clue to my question is that I have use another "template" tableview that is identical to the inner tableview.

Finally I devised a working solution, I posted here for the purpose of helping others who may have the same/similar problem.

Note there is a little trick about calculating the actually row height - you have to include height of the template tableview's header view.

func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat {
    if tableView is DetailsTableView { return tableView.rowHeight }

    if let rowHeight = rowHeights[row] { return rowHeight }

    let item = categorizedItems[row]

    if sizingTableView == nil {
        var objects: NSArray!

        detailsNib!.instantiate(withOwner: nil, topLevelObjects: &objects)
        for object in objects {
            if let sizingCellView = object as? DetailsCellView {
                sizingTableView = sizingCellView.tableView
                sizingTableView.delegate = self
                sizingTableView.dataSource = self
                sizingTableView.isHidden = true
                sizingTableView.enclosingScrollView?.setFrameSize(NSSize.zero)
                self.view.addSubview(sizingTableView)
                break
            }
        }
    }

    print("<", sizingTableView.bounds.height)
    sizingTableView.row = row
    sizingTableView.files = item.files
    sizingTableView.reloadData()
    print(">", sizingTableView.bounds.height)

    var rowHeight = sizingTableView.bounds.height
        + (sizingTableView.headerView?.bounds.height ?? 0.0)
        + summaryTableView.intercellSpacing.height
    rowHeights[row] = rowHeight

    if rowHeight > MaxRowHeight {
        let trailingHeight = (sizingTableView.bounds.height / Double(item.files.count)) * Double(TrailingRowCount)

        if rowHeight - MaxRowHeight < trailingHeight {
            // Try reserve height for some rows to let scrollbar look more natural.
            rowHeight = max(0, MaxRowHeight - trailingHeight)
        } else {
            rowHeight = MaxRowHeight
        }
    }

    return max(rowHeight, summaryTableView.rowHeight)
}