Variable row heights in your
NSTableView might be broken in your apps on macOS Ventura 13.0 – it’s fixed with the upcoming 13.1, but that’s only available as a beta at the moment.
When you replace table contents by calling
aTableView.reloadItems(), this will ingest the new data as usual, but the old row heights won’t be forgotten. This can affect scrolling. The row height cache, it seems, isn’t properly invalidated or cleared.
NSOutlineViewnow automatically estimate row heights for view-based table views whose delegates implement
tableView(_:heightOfRow:)and provide variable row heights. This provides performance improvements for table views with large numbers of rows by reducing the frequency of the calls to
(macOS 13 Release Notes)
Kuba Suder summarized this:
In macOS Ventura
NSTableViewnow calculates the row heights lazily:
- row heights are calculated for rows which are in or near the scrolling viewport
- for the rest of the rows,
NSTableViewestimates the height based on the row heights that it has already measured
- as the table is scrolled, the table view requests more row heights as needed and replaces the estimates with real measurements
@SkipRousseau shared how to fix this in a Slack:
Your app can opt out of row height estimation.
Test this via launch arguments in Xcode by adding
-NSTableViewCanEstimateRowHeights NO. That essentially overrides a
To do this programmatically, call
UserDefaults.standard.set(false, forKey: "NSTableViewCanEstimateRowHeights")early in the app’s launch routine, e.g. during
Invalidate the cache for each row index, in code via
NSTableView.noteHeightOfRows(withIndexesChanged:). Skip’s sample to invalidate the cache for all rows is:
// Right after reloadData, NSAnimationContext.beginGrouping() NSAnimationContext.current.duration = 0 let entireTableView: IndexSet = .init(0 ..< self.tableView.numberOfRows) self.tableView.noteHeightOfRows(withIndexesChanged: entireTableView) NSAnimationContext.endGrouping()
For now, I tend towards disabling
NSTableViewCanEstimateRowHeights by default to get the old behavior and to prevent the bug on Ventura, and optionally enable this for macOS 13.1 during the launch procedure. Opting-out of the new behavior sounds easier to maintain than invalidating row indexes on 13.0 in code.