Sebastian
Sebastian

Reputation: 964

NSTableView with scroll view and header view in SwiftUI

I'm trying to implement an NSTableView in a SwiftUI app in MacOS using NSViewRepresentable. The part of creating the table view works well so far:

struct TableView: NSViewRepresentable {
    typealias NSViewType = NSTableView

    func makeNSView(context: Context) -> NSTableView {
        let tableView = NSTableView(frame: .zero)
        tableView.delegate = context.coordinator
        tableView.dataSource = context.coordinator
        
        tableView.rowHeight = 20.0
        tableView.gridStyleMask = [.solidVerticalGridLineMask, .solidHorizontalGridLineMask]
        
        let nameColumn = NSTableColumn(identifier: NSUserInterfaceItemIdentifier(rawValue: "Name"))
        nameColumn.title = "Name"
        tableView.addTableColumn(nameColumn)
        
        let ageColumn = NSTableColumn(identifier: NSUserInterfaceItemIdentifier(rawValue: "Age"))
        ageColumn.title = "Age"
        tableView.addTableColumn(ageColumn)

        return tableView
    }
    
    func updateNSView(_ nsView: NSTableView, context: Context) {
        nsView.reloadData()
    }

    func makeCoordinator() -> Coordinator {
        return Coordinator()
    }
}

extension TableView {
    final class Coordinator: NSObject, NSTableViewDelegate, NSTableViewDataSource {
        
        func numberOfRows(in tableView: NSTableView) -> Int {
          return 12
        }
        
        func tableView(_ tableView: NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> Any? {
            switch tableColumn?.identifier.rawValue {
            case "Name":
                return "Person \(row)"
            case "Age":
                return "\(25 + row)"
            default:
                return nil
            }
        }
        
        func tableView(_ tableView: NSTableView, shouldEdit tableColumn: NSTableColumn?, row: Int) -> Bool {
          return true
        }
        
        func tableView(_ tableView: NSTableView, setObjectValue object: Any?, for tableColumn: NSTableColumn?, row: Int) {
        }
    }
}

Of course, this only gives me the pure table, without a NSScrollView or a NSHeaderView.

My question is now: which would be the most elegant and SwiftUI-ish way to integrate NSScrollView and NSHeaderView?

  1. bundle all three with NSViewRepresentable (very monolithic)
  2. pull the NSHeaderView from my NSViewRepresentable and somehow set it up inside SwiftUI's ScrollView (not sure how I would achieve that it only scrolls horizontally though)
  3. something else?

Thanks!

Upvotes: 2

Views: 677

Answers (1)

FelipeOliveira
FelipeOliveira

Reputation: 789

Thanks for you question, I used your code as base for my solution. Basically to show the NSTable header you need to wrap the NSTableView in a ScrollView. This is my final code

struct TableView: NSViewRepresentable {
  typealias NSViewType = NSScrollView
  
  func makeNSView(context: Context) -> NSScrollView {
    let tableContainer = NSScrollView(frame: .zero)
    
    let tableView = NSTableView(frame: .zero)
    tableView.delegate = context.coordinator
    tableView.dataSource = context.coordinator
        
    tableView.rowHeight = 20.0
    tableView.gridStyleMask = [.solidVerticalGridLineMask, .solidHorizontalGridLineMask]
        
    let nameColumn = NSTableColumn(identifier: NSUserInterfaceItemIdentifier(rawValue: "Name"))
    nameColumn.title = "Name"
    tableView.addTableColumn(nameColumn)
        
    let ageColumn = NSTableColumn(identifier: NSUserInterfaceItemIdentifier(rawValue: "Age"))
    ageColumn.title = "Age"
    tableView.addTableColumn(ageColumn)

    
    tableContainer.documentView = tableView
    tableContainer.hasVerticalScroller = true
    
    return tableContainer
  }
  
  func updateNSView(_ nsView: NSScrollView, context: Context) {    
    
    guard let tableView = nsView.documentView as? NSTableView else { return }
    tableView.reloadData()
  }
  
  func makeCoordinator() -> Coordinator {
    return Coordinator()
  }
}

Upvotes: 2

Related Questions