Thanks for being a part of WWDC25!

How did we do? We’d love to know your thoughts on this year’s conference. Take the survey here

Remove bottom border in a row in AppKit's NSTableView

I just made a simple AppKit app, but don't know how to remove borders of rows when they're swiped.

SwiftUI's list does not have this problem though.

Attaching gif demo and code:

import SwiftUI

struct NSTableViewWrapper: NSViewRepresentable {
    @State var data: [String]

    class Coordinator: NSObject, NSTableViewDataSource, NSTableViewDelegate {
        var parent: NSTableViewWrapper
        weak var tableView: NSTableView?

        init(parent: NSTableViewWrapper) {
            self.parent = parent
        }

        func numberOfRows(in tableView: NSTableView) -> Int {
            self.tableView = tableView
            return parent.data.count
        }

        func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
            let cell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier("Cell"), owner: nil) as? NSTextField
                ?? NSTextField(labelWithString: "")
            
            
            cell.identifier = NSUserInterfaceItemIdentifier("Cell")
            cell.stringValue = parent.data[row]
            
            cell.isBordered = false
            return cell
        }

        func tableView(_ tableView: NSTableView, rowActionsForRow row: Int, edge: NSTableView.RowActionEdge) -> [NSTableViewRowAction] {
            guard edge == .trailing else { return [] }

            let deleteAction = NSTableViewRowAction(style: .destructive, title: "Delete") { action, index in
                self.deleteRow(at: index, in: tableView)
            }
    
            
            return [deleteAction]
        }

        private func deleteRow(at index: Int, in tableView: NSTableView) {
            guard index < parent.data.count else { return }

            NSAnimationContext.runAnimationGroup({ context in
                context.duration = 0.3
                tableView.removeRows(at: IndexSet(integer: index), withAnimation: .slideUp)
            }, completionHandler: {
                DispatchQueue.main.async {
                    self.parent.data.remove(at: index)
                    tableView.reloadData()
                }
            })
        }
    }

    func makeCoordinator() -> Coordinator {
        return Coordinator(parent: self)
    }

    func makeNSView(context: Context) -> NSScrollView {
        let scrollView = NSScrollView()
        let tableView = NSTableView()

        let column = NSTableColumn(identifier: NSUserInterfaceItemIdentifier("Column"))
        column.width = 200
        tableView.addTableColumn(column)

        tableView.delegate = context.coordinator
        tableView.dataSource = context.coordinator

        tableView.backgroundColor = .clear
        tableView.headerView = nil
        tableView.rowHeight = 50
        tableView.style = .inset

        scrollView.documentView = tableView
        scrollView.hasVerticalScroller = true
        scrollView.additionalSafeAreaInsets = .init(top: 0, left: 0, bottom: 6, right: 0)

        return scrollView
    }

    func updateNSView(_ nsView: NSScrollView, context: Context) {
        (nsView.documentView as? NSTableView)?.reloadData()
    }
}

struct ContentView: View {
    @State private var itemsString = Array(0..<40).map(\.description)

    var body: some View {
        NSTableViewWrapper(data: itemsString)
    }
}

func createAppWindow() {
    let window = NSWindow(
        contentRect: .zero,
        styleMask: [.titled],
        backing: .buffered,
        defer: false
    )
    window.title = "NSTableView from AppKit"
    window.contentViewController = NSHostingController(rootView: ContentView())
    window.setContentSize(NSSize(width: 759, height: 300))
    window.center()
    window.makeKeyAndOrderFront(nil)
}

class AppDelegate: NSObject, NSApplicationDelegate {
    
    func applicationDidFinishLaunching(_ notification: Notification) {
        createAppWindow()
    }
}

let delegate = AppDelegate()
NSApplication.shared.delegate = delegate
NSApplication.shared.run()

Answered by Kopyl in 829183022

What helped: adding another tableView method:

func tableView(_ tableView: NSTableView, rowViewForRow row: Int) -> NSTableRowView? {
    let rowView = MyCustomRowView()
    return rowView
}

class MyCustomRowView: NSTableRowView {
    override func drawSeparator(in dirtyRect: NSRect) {}
}
Accepted Answer

What helped: adding another tableView method:

func tableView(_ tableView: NSTableView, rowViewForRow row: Int) -> NSTableRowView? {
    let rowView = MyCustomRowView()
    return rowView
}

class MyCustomRowView: NSTableRowView {
    override func drawSeparator(in dirtyRect: NSRect) {}
}
Remove bottom border in a row in AppKit's NSTableView
 
 
Q