NSOutlineView doesn't call delegates when wrapped with NSViewRepresentable

93 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
son 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