NSOutlineView doesn't call delegates when wrapped with NSViewRepresentable

85 Views Asked by At

Due to limitations with DisclosureGroup functionality with a List, I'm looking into using NSOutlineView wrapped with NSViewRepresentable.

However, with this simple example, when I create the view I'm not seeing all of the required NSOutlineViewDataSource functions being called on the Coordinator that I assume should populate the view. Only outlineView(_:numberOfChildrenOfItem:) is called, then updateNSView(_:context:) is called after that.

I added a border to check the wrapped view is actually being drawn in the ContentView and it seems to be.

What am I missing?

Wrapping the NSOutlineView:

import SwiftUI

struct AppKitOutlineView: NSViewRepresentable {
    
    //ViewRepresentable Stuff
    
    class Coorindinator: NSObject, NSOutlineViewDataSource{
        
        var theData = ["One", "Two", "Three", "Four"]

        //NSOutlineViewDataSource Stuff
        
        func outlineView(_ outlineView: NSOutlineView, child index: Int, ofItem item: Any?) -> Any {
            print(#function)
            return theData[index]
        }
        
        func outlineView(_ outlineView: NSOutlineView, isItemExpandable item: Any) -> Bool {
            print(#function)
            return false
        }
        
        func outlineView(_ outlineView: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int {
            print(#function)
            print (theData.count)
            return theData.count
        }
        
        func outlineView(_ outlineView: NSOutlineView, objectValueFor tableColumn: NSTableColumn?, byItem item: Any?) -> Any? {
            print(#function)
            return item    
        }
        
    }
    
    func makeCoordinator() -> Coorindinator {
        print(#function)
        return Coorindinator()
    }
    
    func makeNSView(context: Context) -> some NSView {
        print(#function)
        let outlineView = NSOutlineView()
        outlineView.dataSource = context.coordinator
        return outlineView
    }
    
    func updateNSView(_ nsView: NSViewType, context: Context) {
        print(#function)
        //Nothing yet
    }
} 

ContentView:

import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack{
            Text("Thing")
            AppKitOutlineView().border(Color.red)
        }
    }
}

This is what I can see is happening in the console:

makeCoordinator()
makeNSView(context:)
outlineView(_:numberOfChildrenOfItem:)
4
updateNSView(_:context:)

And this is what I can see in the program UI: program UI

1

There are 1 best solutions below

2
On BEST ANSWER

The others DataSource depend on outline view appearance, which return nil in your example. So that's why it's not get called. Try to conform dataCellFor tableColumn in NSOutlineViewDelegate:

func makeNSView(context: Context) -> some NSView {
    ...
    outlineView.delegate = context.coordinator
}

class Coorindinator: NSObject, NSOutlineViewDataSource, NSOutlineViewDelegate {
    func outlineView(_ outlineView: NSOutlineView, dataCellFor tableColumn: NSTableColumn?, item: Any) -> NSCell? {
        if let item = item as? String {
            let cell = NSCell(textCell: item)
            return cell
        } else {
            return NSCell()
        }
    }
    ...
}

Then the output should be:

makeCoordinator()
makeNSView(context:)
outlineView(_:numberOfChildrenOfItem:)
4
outlineView(_:child:ofItem:)
outlineView(_:isItemExpandable:)
outlineView(_:child:ofItem:)
outlineView(_:isItemExpandable:)
outlineView(_:child:ofItem:)
outlineView(_:isItemExpandable:)
outlineView(_:child:ofItem:)
outlineView(_:isItemExpandable:)
updateNSView(_:context:)
updateNSView(_:context:)
makeCoordinator()
makeNSView(context:)
outlineView(_:numberOfChildrenOfItem:)
...

enter image description here