Reputation: 1191
I'm trying to programmatically add columns to a NSTableView
, part of a view I defined with IB as part of a custom view controller that I would like to reuse (and I would customize the columns for each specific use).
I'm using a data source/delegate to provide row/column values. I simplified my code to the minimum code needed to reproduce the issue.
The table shows completely wacky behaviour: I create 1000 rows, but only the first ~17 (those who fit the initial view port) are displayed, the rest, visible by scrolling down, are empty. When scrolling back up, even the initial ones are now gone. In addition, the row views are clipped.
Here's a video of the behaviour: https://www.youtube.com/watch?v=toRpIfp5A6w
For those who can't play the video, here's a screenshot half-way through the scrolling. Keep in mind that the table is supposed to have 1000 rows.
Here's my code.
AppDelegate
import Cocoa
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
@IBOutlet weak var window: NSWindow!
func applicationDidFinishLaunching(_ aNotification: Notification) {
let controller = TestViewController()
self.window.contentView?.addSubview(controller.view)
controller.view.translatesAutoresizingMaskIntoConstraints = false
controller.view.heightAnchor.constraint(equalTo: self.window.contentView!.heightAnchor).isActive = true
controller.view.widthAnchor.constraint(equalTo: self.window.contentView!.widthAnchor).isActive = true
controller.view.trailingAnchor.constraint(equalTo: self.window.contentView!.trailingAnchor).isActive = true
}
func applicationWillTerminate(_ aNotification: Notification) {
}
}
View controller
import Cocoa
class TestViewController: NSViewController {
@IBOutlet weak var tableView: NSTableView!
private var tableSource = TestTableSource()
override func viewDidLoad() {
super.viewDidLoad()
let columns = self.tableView.tableColumns
columns.forEach {
self.tableView.removeTableColumn($0)
}
let column = NSTableColumn(identifier: "c1")
column.title = "COL1"
self.tableView.addTableColumn(column)
self.tableView.delegate = self.tableSource
self.tableView.dataSource = self.tableSource
self.tableView.reloadData()
}
}
class TestTableSource: NSObject, NSTableViewDelegate, NSTableViewDataSource {
let array = (0..<1000).map { "row \($0)" }
func numberOfRows(in tableView: NSTableView) -> Int {
return array.count
}
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
guard tableColumn?.identifier == "c1" else { fatalError() }
let view = NSTextField(string: array[row])
return view
}
}
View Controller in IB
These are the NSTableView settings
and
Any suggestion on how to address the issue is welcome!
EDIT: I forgot to switch to View-based. I uploaded the new screenshot. The problem is still there.
Upvotes: 1
Views: 3109
Reputation: 1191
Solved: the table view delegate/data source was deallocated immediately because nothing was holding a strong reference to it.
Since the delegate is a weak property in NSTableView
, any further attempt at getting the value of a cell would fail.
Long story: TestViewController
was declared as a scope variable, and then its view added to the view hierarchy. But since nothing was holding on to this view controller itself, it was deallocated immediately. And since the view controller was the one holding on to the delegate/data source, this would be deallocated too.
Kudos to @biappi for helping me sort this out.
Upvotes: 1