I need to load data dynamically as a user scrolls through an NSTableView. For example, the table might display 50 rows, and as it scrolls further I need to fetch more data from the network. The number of of objects/rows is known beforehand, so I want the table to have the right number of rows from the start, but showing empty cells while data is loading.
I'm using Core Data so it's easy to connect the table to my model using bindings. This would also take care of cells being updated as data comes in and is parsed. I've tried to figure out how I could do this by subclassing NSArrayController but from what I can tell there is no information flowing from the table to the controller about which rows actually need data. Therefore, I'm thinking of implementing NSTableViewDataSource instead, where I can easily check if the table has scrolled beyond the rows for which data is available. On the other hand, I don't know if I will get cells automatically updating as easily with this solution.
In case anyone comes across this, here's my own answer:
Yes, you need to implement NSTableViewDataSource on a controller, observe changes on the data and call reloadData manually on the table when changes occur. The main reason for doing this is that you can defer loading of data until it's actually needed (when the table view scrolls). Using the data source protocol keeps you informed of which indexes are requested.
Related
I am badly struck in a issue where I am trying to populate the nestableview lazily. Below is my approach.
I have created a custom class PRIList where it has an instance of array to manage the models.
I have bound the priList.items to the array controller in the xib where items is not an instance in PRIList but to support lazy loading I have implemented the methods countOfItems and objectInItemsAtIndex:.
Initialy when I populate the PRIList I populate few objects (say 50) with valid objects and rest with the faulty objects. In the objectInItemsAtIndex I check if the item at particular index is valid or faulty. If it is faulty I fetch next set of 50 objects.
What I understand is NSArrayController calls the method objectInItemsAtIndex for only the visible rows in the table view. But the problem here is as soon as set the PRIList the objectInItemsAtIndex method is called for all the objects. This is even called when some selection is changed in table view (the stack trace shows this method is called from [_NSModelObservingTracker startObservingModelObjectAtReferenceIndex])
Basically I want to fetch the records whenever the user scrolls down in the table view.
I followed the same approach in a different project in Lion. It worked there. Currently I am in Mavericks.
I tried overriding the isCompatibleWithResponsiveScrolling in the custom table view and returned it to NO. Still no luck.
Any help is very much appreciated.
First, have you assigned or bound the sort descriptors of the array controller? Or set any columns to automatically generate sort descriptors? (I'm not sure that latter is relevant. It depends on whether the column is sorted by default.)
In any case, if the array controller feels the need to sort the objects in order to arrange the objects, then it will need to load all of the contents. I was under the impression that it always does so, anyway, although you report that it works.
For an issue like this, I'd recommend that you go for full manual control. That means not using bindings or an array controller. Use a data source.
I've got an application which pulls back data from a database using Core Data, and displaying it in a custom cell in a UITableView, via an NSFetchedResultsController. I'd like to randomly insert a different type of custom cell every now and then (say, between every 10 and 20 cells), which will NOT get its data from that same database, and will be a different subclass of UITableViewCell.
I'm a little stumped on how I get in the middle of the NSFetchedResultsController and the UITableView Data Source methods. I have various options which allow the data that's pulled in to be sorted, filtered, etc., so I can't rely on using indexPath or anything like that.
What's the best approach to doing something like this? I know I can access fetchedObjects of the NSFetchedResultsController – is copying and modifying that the right way forward? Create, say, fetchedObjectWithInterstitialCells, and feed all of the Data Source and Delegate methods with the contents of that array?
Is there a better way / are there alternative ways to do it? I'll need to be able to retain the ability to sort / modify / filter the data from the database, while at all times keeping these interstitial cells at that same interval of randomly between every 10 and 20 cells.
I would consider one of the following three operations:
A: Insert objects into the database that are pulled along with the other data, but has properties to differentiate them enough to display the different subclass of the UITableViewCell. Probably the easiest way out, unless you have a very difficult datamodel.
B: If you can group your data from the data store into sections by using the sectionNameKeyPath-attribute on the NSFetchedResultsController, go for the NSFetchedResultsController + UITableViewDataSource-approach, and then return [[self.fetchedResultsController sections] count] + numberOfInterstitialCells; inside numberOfSectionsInTableView:.
In cellForRowAtIndexPath you would then need to override the section-info from the indexpath in order to switch between the right object from FetchedResultscontroller and your interstitial cells. This is probably a cumbersome and difficult way to do it, but if you are on iOS6 using parentContexts, the benefit from having implemented the NSFetchedResultsControllerDelegate is awesome
C: Fetch the objects with a normal fetchrequest and put them into a mutable array in which you insert your interstitial cells, before you load your view and feed your UITableViewDataSource with this array. As easy as option A, but you won't get the benefits of having the NSFetchedResults-Controller.
NSTableView reload automatically when i resize window and i calculate something when table reload,now i have problem because it's reload automatically.how to disable it? i found below answer but doesn't work
[self.tbl setFocusRingType:NSFocusRingTypeNone];
You can't. A table view does not have internal storage for the values in the table. It uses the data source as its storage. If resizing the table view requires that it draw new rows or columns, or even redraw existing rows and columns, it must consult the data source to obtain the information necessary to do that.
What you're learning is that it is inappropriate to do expensive calculations in the data source methods. From the documentation for -tableView:objectValueForTableColumn:row::
tableView:objectValueForTableColumn:row: is called each time the table
cell needs to be redisplayed, so it must be efficient.
(Emphasis added.)
I am trying to retrieve a specific NSCell data from an NSTableView through [NSTableColumn dataCellForRow] but every time it shows different value for same row and same column. The data source remains the same at all time. The NSTableView is bound to an NSArrayController .Please if anyone can suggest a better way to do it correctly. And I don't want to implement those delegate methods. Let me know if you require more information.
Regards
Well, this is not a good idea to get data from a cell. Cells represent View concept in Model-View-Controller paradigm.
You should be able to access all the data that a cell can access, so get it from the primary data source.
I've run into a bit of an issue with NSTableView. The problem is that the table view is requesting data too early (it immediately requests the data from the controller), as in, the data is not there when the table view asks for it. Other methods, which gather the data to be shown in the table view haven't finished executing by the time NSTableView requests the data.
Hope that makes some kind of sense! I'm using Objective-C and doing mac dev (not iphone) btw.
Anyway, is there any way around this?
Thanks in advance.
The NSTableView is asking for the data likely because you have told it that there are say 20 rows of data. Once it knows there are 20 rows, it will start asking for it.
If you are using the NSTableView data source Delegate, its easy, just return 0 for the number of rows until everything is ready - then return the number of rows that there are.
IE you need these calls in the delegate:
– numberOfRowsInTableView:
– tableView:objectValueForTableColumn:row:
If you are not using the NSTableViewDataSource, and have wired up the controller in Interface Builder, then you will have to somehow only give the controller the data when its all ready.
Sounds like you're populating the table view asynchronously? If so you could call reloadData or reloadDataForRowIndexes:columnIndexes: on your table view in the method that handles the response to refresh the table view once the data has loaded.