Display a list of values in an NSTableView/NSOutlineView cell?

Background:

I'm targeting macOS 11 and up, running macOS 12.4 (21F79), and working with Xcode 13.4 (13F17a).

I am building an application to show a list of VMs running under my account on a hosting provider. The VMs have a lot of simple properties like a UUID, a name, a comment field, a power state, and so on. They also have a few properties which are lists of relationships to other things. For example, a single VM can have several disks or several network adapters.

The VMs can be organized in folders, so I'm trying to use NSOutlineView bound to a tree controller to show the downloaded data. The problem I have is that when I scroll my outline, it has significant animation hitches. Instruments tells me they are due to expensive commits.

I originally built the NSOutlineView programmatically, but I've switched to building it in Interface Builder to get the NSTableCellView reuse behavior without needing to subclass and write my own implementations. I have an NSView subclass for the disk column's views and the network adapter column's views. These views present an objectValue property, which gets modified as expected. They then contain a SwiftUI VStack containing a ForEach which renders a row for each disk/interface. View reuse is working, and the internal SwiftUI view properly observes the field for new or removed disks/interfaces. It also properly observes the disk/interface objects themselves for changes (e.g, renaming).

Scrolling still has hitches. The hitches get far worse when I bump the number of columns up to the 20 or so I want to use in the real application.

I have built a small example project with an object factory instead of an API connection. The full application has a set of different icons representing different types of disk and network for different performance tiers. For now, I have simplified that to a single icon for each:

https://github.com/Zimmie0/TableInOutlineExample

My questions:

Is there a better way to show a list of items with an image and a string of text per item inside a single NSOutlineView or NSTableView cell?

Maybe instead of the SwiftUI stuff in the reusable views, I should be building an NSArrayController, an NSTableView with a single column, and using bindings and value transformers (to get an image from the text field indicating the resource's performance tier)? I'm not sure how to do that, though. I specifically don't want a scroll view and a clip view inside the top-level table cells. The top-level table rows should be tall enough objects are never hidden. I'm not sure how to build an NSTableView in interface builder (to get the NSTableCellView bindings and so on) without the scroll view and clip view around it. Also not sure how to tell the system to show the view from some XIB.

Could the problem be related to usesAutomaticRowHeights? I'm aware that hurts performance, but I don't know to what extent. I can determine the row height myself easily enough, but I'm not sure how to tell the outline view that a given row's height needs to change in response to a disk or network adapter being added or removed.

Replies

I've managed to improve performance somewhat. Modifications are in the most recent commits on the repo.

  1. Added a property to my Core Data objects which computes the height of that row from only the object count in the relevant fields
  2. Implemented NSOutlineViewDelegate.outlineView(_:heightOfRowByItem:) to get the height from the object
  3. Set up my outline view delegate to observe NSManagedObjectContext, get the index of every row the outline view is showing, and tell the outline view to recompute those rows' heights
  4. Disabled usesAutomaticRowHeights

Before the modifications, I got 172ms mean hitch duration, 168ms median. Afterwards, I get 87ms mean 88ms median.

Scrolling performance still isn't great, but that may be down to my development machine being on the older side.