How to update NSTableView without using -reloadData?
Asked Answered
I

5

18

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.

Illgotten answered 20/10, 2010 at 11:52 Comment(3)
Let me clarify a Bit more, I don't want the table to be redrawn. I want some method in which I can pass the row_number, column_number or NSTableColumn, and the (id)dataobject.Illgotten
@Mike Abdullah I would have appreciated had you proposed a solution instead!Illgotten
His first sentence already made that perfectly clear. Seriously, why be a dick?Rightism
T
29

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]];
Trigonous answered 20/10, 2010 at 11:59 Comment(2)
Docs link: developer.apple.com/library/mac/documentation/Cocoa/Reference/…:Cowden
While this is the correct solution it should be mentioned that reloadData() also just re-renders the currently visible cells and not the entire table.Minute
V
18

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.

Vernellvernen answered 6/11, 2010 at 10:52 Comment(0)
M
3

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)]
];
Moskow answered 12/9, 2016 at 15:42 Comment(1)
In your second example, reloadDataForRowIndexes:columnIndexes: can't take an NSRange, so you need to do something like: [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, _tableView.numberOfColumns)]Witherite
Y
0

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))
    }
}
Yung answered 9/10, 2018 at 21:26 Comment(0)
G
-5

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.

Gearard answered 20/10, 2010 at 22:28 Comment(1)
I want to do this because of efficiency. Like I need to update 8-10 cells per second it this case I dont want the whole table to be updated over and again !Illgotten

© 2022 - 2024 — McMap. All rights reserved.