Search code examples
cocoansoutlineview

NSOutlineView reloadItem/reloadData not working when replacing an item


I have a view-based NSOutlineView with a dataSource/delegate model instead of binding to a tree controller (I want control over the insert/update animations).

I'm replacing an item in my model and would like to update that specific row in the outline view without having to call reloadData().

I cannot get this to work. Either the item does not update at all or the item's expanded state doesn't update. There seems to be some caching being done inside of NSOutlineView according to this, but even with these suggestions, I could not get it to work.

What I have:

(1) The outline view represents a folder structure

(2) At first, there is a singe file:

file

(3) The file is then replaced with a folder item:

 // Model update
 let oldFileItem = rootItem.children.first!
 rootItem.children.remove(at: 0)
 rootItem.children.append(Item(title: "Folder", children:[], isExpandable:true))

Expected result:

expected folder

Actual result (reloadItem):

outlineView.reloadItem(oldFileItem) // I kept a reference

folder no expansion state

Icon and title reloaded, but note that the expansion triangle is missing.

I can somewhat understand that reloadItem() might not work in this case, because the old item is not part of the data model anymore. Strangely enough, the item's title and icon update, but not the expansion state.

Actual result (reloadData(forRowIndexes:columnIndexes:):

outlineView.reloadData(forRowIndexes: IndexSet(integer:0), columnIndexes: IndexSet(integer:0))

file

No effect whatsoever. This is the one that I would have expected to work.

Actual result (remove/insert):

outlineView.removeItems(at: IndexSet(integer:0), inParent: rootItem, withAnimation: [])
outlineView.insertItems(at: IndexSet(integer:0), inParent: rootItem, withAnimation: [])

file

No effect whatsoever.

The docs say about removeItems(): "The method does nothing if parent is not expanded." and isExpanded does indeed return false for the root node, although its children are visible. Is this special behavior for items that are direct children of the root node? What am I missing here?

For reference, my data model:

class Item:NSObject {
    var title:String
    var children:[Item]
    var isExpandable:Bool

    init(title:String, children:[Item], isExpandable:Bool) {
        self.title = title
        self.children = children
        self.isExpandable = isExpandable
    }
}

Solution

  • For reference:

    It turned out to be an issue with how I used the API. NSOutlineView.removeItems/insertItems expect nil for the inParent parameter for the root item. I was handing in the actual root item. Using nil instead of the root item solved the problem.