I am using a FetchedResultsController to fetch the data for my UITableView. The data is created via actions performed on another tab (so my table may have 5 items but if I switched tabs and go back to my table, it may have more than 5 items that it should display). My table can potentially contain many rows. Right now I am using [myFetchedResultsController performFetch] in my viewDidLoad.It appears that when I create data in my other tab, when I switch back to my table tab, that new data is put into my table automatically without me perfomring [myFetchedResultsController performFetch] again. Here are my questions:
1) Does a fetchedResultsController automatically monitor the manajedObjectContext for changes and fetches the new objects if they come into existence? (This appears to be what is happening but I just want to make sure. Perhaps I have some code that is helping me do this that I forgot I put in somewhere)
2) Does the fetch performed by [myFetchedResultsController performFetch] fetch all of the objects at that time, or does it fetch only what it can fit in the view of the table and it fetches the rest later as it needs it (as you scroll in the table)? I ask because since my table can potentially have a lot of rows, it seems inefficient to fetch all the data at once if only ~12 of them will be displayed on the table at once.
EDIT: I just realized that in my FetchedResultsController delegate methods, I have
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
[self.tableView reloadData];
}
Am I correct in saying that a fetchedResultsController monitors for change, but will not apply it to the table unless the table is reloaded as I have done? If so, then I have another question about UITableView. Does reloading the table only reload the rows in view and then the other rows are updated as you scroll? Again I ask because if my data is very large, it seems inefficient to reload the entire table if it will reload all rows at once.
Yes, if you add a delegate
You should set the fetch request batch size when you configure the FRC because it can only load an appropriate number of items for the screen if you tell it how many that is.
You apply the changes, the FRC just collects and supplies the data. The delegate method tells you about a change. Reloading affects the whole table in terms of row count but only shows the visible rows (assuming the batch size is set appropriately).
Related
Description
I have a CoreData entry called Person, I fetch it using NSFetchedResultsController, with a fetchRequest ordered by property "name". Then I display the "name" in the table view cell.
Problem
When I change the entity's "name" property and the rows reorder, NSFetchedResultsControllerDelegate does give me a NSFetchedResultsChangeType.move. But the "name" displayed on the cell is outdated, meaning I'm not receiving NSFetchedResultsChangeType.update
The Table View Programming Guide: Inserting and Deleting Rows and Sections says batch updates do updates first, then deletions, lastly insertions.
It defers any insertions of rows or sections until after it has handled the deletions of rows or sections. The table view behaves the same way with reloading methods called inside an update blockāthe reload takes place with respect to the indexes of rows and sections before the animation block is executed. This behavior happens regardless of the ordering of the insertion, deletion, and reloading method calls.
Question
Any idea on how the notifications sent by NSFetchedResultsController are implemented, specifically on the ordering of insert, delete, update & move?
Or how can I use some kind of code to efficiently (meaning partial update, not reload all data) solve this particular problem?
Your setup is non-standard. The sectionNameKeyPath is really meant for sections not rows. Fetch the Person entity and populate the cell with a person's name directly via itemForRowAtIndexPath.
You will then have the expected change types available.
I have a view which shows a list of entries in a UITableView. The data for the list is an NSArray. When the user is on this view, there is an asynchronous call to a webservice to retrieve the latest entries. When I have downloaded the latest entries, I update the NSArray and then I send off a notification to the notification center. The UITableView is listening to this event and will try to reload the table.
But with this approach I get an error which is on the lines of "The object was mutated during reading".
After going through similar questions here on SO, I see the suggested approach is to remove objects and add objects at individual indexes and ask the table view to reload the data.
Is there a way to replace the array completely and ask the table view to reload the data ?
UITableView's reloadData method should do the trick.
From Apple's documentation:
Call this method to reload all the data that is used to construct the
table, including cells, section headers and footers, index arrays, and
so on. For efficiency, the table view redisplays only those rows that
are visible. It adjusts offsets if the table shrinks as a result of
the reload. The table view's delegate or data source calls this
method when it wants the table view to completely reload its data. It
should not be called in the methods that insert or delete rows,
especially within an animation block implemented with calls to
beginUpdates and endUpdates
https://developer.apple.com/library/prerelease/ios/documentation/UIKit/Reference/UITableView_Class/index.html#//apple_ref/occ/instm/UITableView/reloadData
if you have Updated NSArray Object
Then you can use directly following method of UITableView
[tableView reloadData]
I have to fill a TableView with lots of items (20.000 items frol sqlite) but I know that I will overload it.
Is there a way to load only the needed data when scolling ?
for example if I display 100 items when I scroll over 100 should I load 200 in the table or load only the items 101>200 ?
Also is it possible when returning the Cell content to fetch for EACH element in the embedded sqlite DB ?
Thanks
I have to fill a TableView with lots of items (20.000 items frol sqlite) but I know that I will overload it. Is there a way to load only the needed data when scrolling?
UITableView is designed specifically to load only the cells that are visible. You don't so much "fill" a table as you make the data available to the table via the table's data source. As the table scrolls, it asks its data source for additional cells, and it re-uses the cells that are no longer visible. This minimizes memory usage and maximizes speed. You don't need to do anything to achieve this other than implement the necessary table view data source and delegate methods in your view controller (or some other object).
Also is it possible when returning the Cell content to fetch for EACH element in the embedded sqlite DB ?
Sure, you can do that. Just implement your -tableView:cellForRowAtIndexPath: method to make the appropriate SQLite query for the requested cell.
This the default functionality of a UITableView, it only draws the necessary rows when they need to appear. This is why you have "reusable cells" because they keep getting reused. I think storing the 20,000 items should not be terrible memory overhead, so I would just try using the UITableView default functionality and you will be fine.
I've a UITableViewDataSource which maintains sections of data items which will be presented by an UITableView instance. The table view is editable, allowing insertion and deletion of rows and sections, and all changes on the view should be written back to the data source. After reading through the Apple documents, I can deal with insertion and deletion on rows by sending message tableView:commitEditingStyle:forRowAtIndexPath: to the data source.
But, however, I can't figure out what is the standard way to feedback the changes on sections to the data source. Please kindly help.
The delegate method tableView:commitEditingStyle:forRowAtIndexPath: is called by the table view to tell you what the user has done (what they have added / removed). It is then the responsibility of your code to make the appropriate changes to your Model and reload the table view.
The simplest way to reload is to call reloadData, and you also have more specific options for reloading / inserting / removing individual rows or sections - this is all from a UI perspective and the table view requires that you have updated your Model before you call any of these methods or the table view will throw an exception.
I have a UITableView that collects data from a database. What I would like to know is if there is some way I can iterate in the UITableView collection and check the values of the cell? The reason I ask is because I would like to update each cell based on the current value that it has (change font, size, color, etc.). I've seen in another SO post regarding this topic, but since the cells are already created and their values are changed it is a bit harder for me. I was thinking of iterating through the UITableView before I call reloadData, but any other suggestions are welcome.
You should not iterate over the cells of UITableView, because some of them (in fact, most of them) may not be present until you request them. UITableView aggressively recycles its cells, so if a cell is not visible, it is very likely that you would be creating it from scratch only to put it back into recycle queue moments later.
Changing your model and calling reloadData the way your post suggests would be the right solution. iOS will ensure that it runs the update in a smallest number of CPU cycles possible, so you do not need to worry about the cells that are already created. This is also the easiest approach in terms of your coding effort.
A table view is for displaying data. The properties of your table cells should only be written to, not read from. The appropriate way of handling this situation would be to update your underlying model objects -- the objects that you use to populate the table view -- as the data changes, and then reload the affected rows.
The issue you'll encounter is that UITableView reuses table cells. Once a table cell scrolls off the screen, it's quite likely that the table view will reuse the same cell to display a different row.
This means it's fundamentally not possible to iterate over the table cells. When you need to refresh a row because its data has changed, you should call reloadRowsAtIndexPaths:withRowAnimation: (or reloadData if all rows have changed) and if the row is visible on screen, UITableView will call your data source methods and give you an opportunity to configure the cell for display.