I am new to the Mac. I am trying to update a particular cell in NSTableView without using -reloadData
, as -reloadData
updates the whole table. I have tried everything but all was in vain. I am trying to do something similar to what we used to do in CListCtrl in MFC or in .NET.
Have a look at reloadDataForRowIndexes:columnIndexes:
method. To update a single cell the following should work:
[yourTable reloadDataForRowIndexes:[NSIndexSet indexSetWithIndex:row]
columnIndexes:[NSIndexSet indexSetWithIndex:column]];
This is a fundamental difference between the way views typically work in Cocoa and how they work in some other frameworks. NSTableView does expose a -reloadDataForRowIndexes:columnIndexes:
API, as Vladimir points out, but you'll notice the absence of a "withObject:" parameter.
This is because Cocoa views and controls are not designed to also act as data containers. Rather, they're for presentation of and interaction with data that is managed by some other model object, usually through an intermediate controller of some sort.
Given a table view, for example, if your model has informed your controller that some particular data has changed, the controller can invalidate the displayed portion of the table that maps to that value - if there even is a displayed portion of the table. The next time it draws itself, the table will ask its data source (the controller) for the values to present for the area it needs to redraw.
Thus despite a superficial similarity in user interaction, you'll probably want to reconsider how you write the code to implement your user interface. You'll wind up with much more factored Model-View-Controller style code in the end; the price is that you'll need to actually represent your model more thoroughly than you might have had to in frameworks that didn't require such a separation of concerns.
A full example of using reloadDataForRowIndexes to update all rows but only a single column:
int col = 2 ;
[self.myTable reloadDataForRowIndexes:
[NSIndexSet indexSetWithIndexesInRange:
NSMakeRange(0,self.myTable.numberOfRows)]
columnIndexes:
[NSIndexSet indexSetWithIndex:col]
];
Or all columns for a single row:
int row = 2 ;
[self.myTable reloadDataForRowIndexes:
[NSIndexSet indexSetWithIndex:row]
columnIndexes:
[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, self.myTable.numberOfColumns)]
];
reloadDataForRowIndexes:columnIndexes:
can't take an NSRange, so you need to do something like: [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, _tableView.numberOfColumns)]
–
Witherite If your table row is smart enough to update itself with a draw, you can do something like this. This ViewController method is responding to a NSNotification post by a source object that has updated itself.
Swift 4 code:
class ObjectChooserListViewController: NSViewController, NSTableViewDataSource, NSTableViewDelegate {
@IBOutlet weak var tableView: NSTableView!
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(objectUpdating(_:)), name: NSNotification.Name("ObjectUpdating"), object: nil)
// more stuff happening
}
@objc func objectUpdating(_ notification: NSNotification) {
guard let object = notification.object as? MyCoolObject else {return}
guard let row = ObjectManager.shared.getRow(of: object.id) else {return}
guard let cellView = tableView.view(atColumn: 0, row: row, makeIfNecessary: false) else {return}
cellView.draw(tableView.frameOfCell(atColumn: 0, row: row))
}
}
Usually, updating the whole table via reloadData:
is the correct design pattern. Why are you averse to doing so? Is it for efficiency reasons, or are you relying on the table to save some state and reloading all the data interferes with that? The latter is usually a sign that you need to rethink your architecture.
© 2022 - 2024 — McMap. All rights reserved.