Is -[UITableView reloadData] asynchronous or synchronous? - ios

When you call -reloadData on a table view instance, does it make all UITableViewDataSource calls async?
Thank you.

This method actually just removes all the UITableViewCell views from the table. The data source delegate methods are called when the table is repainted.
So, it's asynchronous.
Edit:
Actually, some calls ARE synchronous. The number of sections & rows and row heights are updated immediately, so, for example contentSize is set correctly after the call. On the other hand, tableView:cellForRowAtIndex: is not called until the table is repainted.

Related

Changing my tableview data source every time the view appears. Where should I do it?

I declare an array at the beginning of my UITableViewController:
class ArchiveTableViewController: UITableViewController {
var dataSource: [[Book]]!
(…)
And then I have a function getDataSource() that updates the array from a database.
My first thought was to call it at viewWillAppear, but it seems that the table view loads before that, so it ends up not matching the array.
I could call the function from every single table view method, but that seems a little stupid. So where is the best place to do it? It must get called every time the view appears, so viewDidLoad won't work, but it must get called before the tableview methods, so viewWillAppear won't work either. It's like I need something in between. Or is there a better way to do what I want?
Thanks in advance,
Daniel
Edit: I should have added, that dataSource array is made up of other arrays, each representing a section in the table view. I get the number of sections and the number of rows in a section from the array too, so it must stay the same throughout the tableview methods or the app will crash, it might try to populate a row that shouldn't exist anymore, for instance.
Edit 4: Ah, ok, I got it! I'm sorry for wasting everyone's time looking for complicated answers. I just had to call getDataSource() from both viewDidLoad and viewWillAppear, and call tableView.reloadData() only in viewWillAppear.
I was trying all combinations of where to put things and with reloadData inside getDataSource() it would sometimes get called repeatedly forever until it crashed… or on other attempts I would call getDataSource() from viewWillAppear and not from viewDidLoad, and then it would crash the first time… anyway, I missed the most obvious combination and now I can't understand how I didn't see it before. Thank you all so much for your time.
Set it in override func loadView().
EDIT: As a general practice this is how you should go it:
Show loading overlay on screen while table data is being fetched.
Once data is available:
2.1. remove the loading overlay.
2.2. update data source model.
2.3. reload your table view so latest model could be picked.
Are you are showing other view controller and then going back to the one with tableView and that's why you want to update the tableView every time the view appears?
If so - it should just work the way you initially tried, if you put the getDataSource in viewWillAppear() and it calls tableView.reloadData() in the end (as you have it here in the source sample) then it will effectively show just what you have prepared for it (all the tableview methods will be called again after reloadData() even if they were already called before).
So I would advise debugging why it is not working when you call it from viewWillAppear(). Please add some screenshots or other data of how it is not correct with this method.
As a sidenote, if getDataSource() takes significant time, it may delay showing your view controller if you put it in viewWillAppear. In such case you'll need to do the way #Abhinav laid out for you (but you can start it from viewWillAppear).
Update: You're right, depending on how you've written numberOfSectionsInTableView() and tableView(_, numberOfRowsInSection) you may need to call getDataSource() also from viewDidLoad() or else you may crash because you're not handling empty datasets.
In that case you don't need tableView.reloadData() because table view methods will be called anyway, so as you noted, it's good to separate it from your getDataSource().
Regarding getDataSource() called repeatedly until crash - it probably happened when you added getDataSource() in one of UITableViewDataSource methods (like numberOfSectionsInTableView()) because reloadData() inside getDataSource() would trigger this method again.
Every function needs to access your var dataSource. So that's where the call to getDataSource must go: You create an optional variable that contains either dataSource or is nil, and then you give dataSource a getter which returns that optional variable unwrapped if not nil, and calls getDataSource otherwise.

Using reloadRowsAtIndexPaths when the update of a NSFetchedResultsController is triggered

I have some rows of my UITableView that get updated and then trigger the method controller:didChangeObject:atIndexPath:forChangeType:newIndexPath of the NSFetchedResultsControllerDelegate delegate.
I usually update the rows in the tableView using reloadRowsAtIndexPaths with an animation, but it happens that sometimes the method is triggered because of a change that is not visible and is that case, I don't want to reload the row with an animation.
The method doesn't give the previous state of the object. Is there a way to do it (I didn't find any method of the delegate that might do)?
If your delegate is also your TableViewController, you could just check your UITableView's data source at the IndexPath. The reloadRowsAtIndexPaths method knows the index.

UITableView reloadData and cellForRowAtIndexPath - interruptable?

Let's assume there exists a UITableView called myTable. Let's say the following sequence of events occur --
Table is fully populated.
The data source is updated (in this case an sqlite3 db, but doesn't really matter), an NSNotifcation happens to indicate that there is new data, the main thread receives the notification and then calls [myTable reloadData].
myTable's UITableViewDataSource's numberOfSectionsInTableView gets called. Assume only 1 section.
myTable's UITableViewDataSource's tableView:numberOfRowsInSection: gets called.
Several tableView:cellForRowAtIndexPath:'s get called depending on the number of rows in the data source.
My question has to do with the interruptability of cellForRowAtIndexPath.
The question --
If a "reloadData" is called and cellForRowAtIndexPath's are getting called AND another reloadData comes in, will the cellForRowAtIndexPath's stop midstream and restart after the calls to numberOfSectionsInTableView and numberofRowsInSection?
Or will the new reloadData be queued, such that all of the cellForRowAtIndexPaths will finish BEFORE the numberOfSectionsInTableView and numberOfRowsInSection get called?
I've looked and have been unsuccessful in finding an answer to this question in docs or the net. I'm concerned that if the data store is updated while the cellForRowAtIndexPaths are "running", the numberOfSectionsInTableView can change which can result in the cellForRowAtIndexPath requesting data for a cell that's now out of range.
Any help would be greatly appreciated.
Since you should only call reloadData on the main thread and since by the time a call to reloadData is done, all of the visible cells have already been loaded, there is no problem to worry about. The second call to reloadData will be made after the 1st one is done.
I don't think it is possible to be in the middle of cellForRowAtIndexPath when reloadData is called assuming they are both called on the same (main) thread (having an async processing on background thread at cellForRowAtIndexPath seems meaningless).

When is tableView:numberOfRowsInSection: called in UITableView?

tableView:numberOfRowsInSection is sent to the delegate of a UITableView to find out how many rows it needs to have in a given section.
My question is, when and how often is this method called?
The method is called very first time the tableview is getting loaded and if you are more interested in the delegates then put a breakpoint and check when and where which delegate is called and how many times.
Below are the instances when that function will get called,
For the first time when table is loaded
the time you reload the table data
the time you add/update/delete your row or sections dynamically.
The method - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section is a protocol method of the UITableViewDataSource - protocol. It will be called
the very first time your table view is loaded based on that you have set the dataSource properly, e.g.
self.yourTableView.dataSource = self;
If you are interested in updating your table again at a later time you can call
[self.yourTableView reloadData];
in order to reload the entire table. If you are only interested in reloading a part of your table you can do something similar to
NSIndexSet *reloadSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [self numberOfSectionsInTableView:self.yourTableView])];
[self.yourTableView reloadSections:reloadSet withRowAnimation:UITableViewRowAnimationAutomatic];
Hope it helps!
My question is, when and how often is this method called?
Short Answer : When your UITableView needs to update something.
Long Answer : Delegates Methods generally called themselves however it may be called multiple times when your UITableView needs to update something. By default, it's called very first time the tableview is getting loaded or updated (reloaded).
It depends on how often user will scroll UITable view to section and how many sections there are. This value, which is returned by this function and is casched. Method will need be revoked if you will update content of table view (filtering results, or updating data via reloadData).
Best thing for you will be to add logging to this function and check this yourself.

uitableview reloadData - how do we table refresh is done?

I want to do a reloadData on a UITableView and after the table refresh is done, I want a delegate method to be called when I can know that the refresh is done. How do we know that ?
-reloadData blocks execution—your code won't continue past that call unless you're doing it on a separate thread. It should only be configuring nine cells or so at most; is there some reason you expect that to take a long time?

Resources