I have a 2 column, cell based NSTableView managed by an NSArrayController.
The view is linked to an NSSearchField so is filtered based on a predicate that compares the search string to the strings in column 2.
The view is displaying correctly and filters as intended when the search string is changed.
I need to only allow the users to enable a maximum of 5 rows. To accomplish this, I am monitoring the backing array for changes and keeping track of the amount of enabled entries. If the count exceeds 5, then I inform the users with an alert and deselect the previously enabled entry by getting the selected row index and then disabling that index in the backing array. I do this in a [willChange.. ..didChange] KVO block.
This works as intended when the view isn't sorted, but obviously causes issues when it is because the selected row does not correspond to the appropriate index in the array anymore.
Therefore, I use the following code to get the string value of the selected cell if the view is filtered (Search string is not empty). Then I traverse the array and disable the entry matching the string.
NSInteger selectedRow = [_tv_TableView selectedRow];
NSTableColumn* column= [_tv_TableView tableColumns][1];
NSCell* cell = [column dataCellForRow:row];
NSString* cellValue = (NSString*)[cell stringValue];
return cellValue;
Although the selected row is always accurate, getting the value from the representative cell is not. It appears that initially the returned value is correct, but then selecting other rows returns data from the following row even though the selected index is still correct. I have also observed it returning values from several rows down in the view.
After further testing it appears that for the first time a filter is applied it will work, but filtering by a different string retains the cell value of the first selected cell for that particular falter.
i.e:
However, there are still occasions where the following row value is returned without it being the previously selected item.
My questions are:
Thank you for your time.
I resolved this by changing the code that gets the cell from:
NSInteger selectedRow = [_tv_TableView selectedRow];
NSTableColumn* column= [_tv_TableView tableColumns][1];
NSCell* cell = [column dataCellForRow:row];
NSString* cellValue = (NSString*)[cell stringValue];
return cellValue;
To:
NSCell* cell = [_tv_TableView preparedCellAtColumn:1 row:row];
NSString* cellValue = (NSString*)[cell stringValue];
return cellValue;
The easy solution is to bind the table's selectionIndexes
binding to the array controller's property of the same name. Then, you can access the array controller's -selectedObjects
to find, well, the selected objects.
When you have a row (or index) from the table, whether it's selected or not, you can use the -arrangedObjects
of the NSArrayController
. That will always correspond to the indexes.
Cells are reused. There's not a single cell associated with each (row, column) location. There's a cell associated with each column, but it's reconfigured for each row each time a row needs to be drawn or otherwise manipulated. That's why you need the prepared cell. That said, the cells are conceptually at the view layer (in Model-View-Controller) terms. What you're doing should be in the controller and model domain, so you shouldn't consult the view layer (and shouldn't need to).