Reloading a UICollectionView using reloadData method returns immediately before reloading data - objective-c-blocks

I need to know when reloading a UICollectionView has completed in order to configure cells afterwards (because I am not the data source for the cells - other wise would have done it already...)
I've tried code such as
[self.collectionView reloadData];
[self configure cells]; // BOOM! cells are nil
I've also tried using
[self.collectionView performBatchUpdates:^{
[self.collectionView reloadData];
} completion:^(BOOL finished) {
// notify that completed and do the configuration now
}];
but when I reload the data I am getting crashes.
How can I reload the data into the collection, and only when it has finished reloading - do a particular completion handler

This is caused by cells being added during layoutSubviews not at reloadData. Since layoutSubviews is performed during next run loop pass after reloadData your cells are empty.
Try doing this:
[self.collectionView reloadData];
[self.collectionView layoutIfNeeded];
[self configure cells];
I had similar issue and resolved it this way.

If you'd like to perform some code after your collectionView has completed it's reloadData() method, then try this (Swift):
self.collectionView.reloadData()
self.collectionView.layoutIfNeeded()
dispatch_async(dispatch_get_main_queue()) { () -> Void in
// Put the code you want to execute when reloadData is complete in here
}
The reason this works is because the code within the dispatch block gets put to the back of line (also known as a queue). This means that it is waiting in line for all the main thread operations to finish, including reloadData()'s methods, before it becomes it's turn on the main thread.

Collection view is not supported to be reloaded animatedly with help of reloadData. All animations must be performed with methods, such as
[collectionView deleteItemsAtIndexPaths:indexesToDelete];
[collectionView insertSections:sectionsToInsert];
[collectionView reloadItemsAtIndexPaths:fooPaths];
inside of performBatchUpdates: block. That reloadData method can only be used for rough refresh, when all items are removed and laid out again without animation.

Related

EXC_BAD_ACCESS error at viewwillappear using scrollToRowAtIndexPath

I'm getting an EXC_BAD_ACCESS error when using scrollToRowAtIndexPath in the viewWillAppear method. I searched for solutions and saw some old posts recommending to set delegate and table to nil (see code below), however when I set that I simply dont get anything loaded in my tableview.
I should say that this is part of a chat application where I want to show the last message entered first. Many thanks for any assistance with this.
Here's my viewWillAppear:
-(void)viewWillAppear:(BOOL)animated {
[self.table reloadData];
int lastRowNumber = [self.table numberOfRowsInSection:0] - 1;
NSIndexPath* ip = [NSIndexPath indexPathForRow:lastRowNumber inSection:0];
//self.table.delegate = nil;
//self.table = nil;
[self.table scrollToRowAtIndexPath:ip atScrollPosition:UITableViewScrollPositionTop animated:YES];
}
I should add that this code seems to work fine in other parts of my program, the only time I get the error is in the viewWillAppear method.
viewWillAppear: is too early to do any animation on view. Per Apple Documentation:
This method is called before the view controller's view is about to
be added to a view hierarchy and before any animations are configured
for showing the view. You can override this method to perform custom
tasks associated with displaying the view. For example, you might use
this method to change the orientation or style of the status bar to
coordinate with the orientation or style of the view being presented.
If you override this method, you must call super at some point in your
implementation.
So, you cannot add animations when even the view hierarchy is not set completely.
It may help you
NSIndexPath * lastIndex =[NSIndexPath indexPathForRow:yourContenetArray.count-1 inSection:0];
[self.yourTableView scrollToRowAtIndexPath:lastIndex atScrollPosition:UITableViewScrollPositionNone animated:YES];
Write your code in viewDidAppear method.As your table view is loaded
then after you should call the method scrollToRowAtIndexPath . Then
your animation will be performed.And if you want to use in
viewWillAppear , then You can try to reload your tableView and then
write code for scrollToRowAtIndexPath
You should probably use the viewDidLoad method for scrolling, and if your array count is 0 (empty array) it will throw an error. It is not possible to scroll to cell at index -1.

When calling [self.tableView reloadData]; i get stuck in a loop

I have a UITableViewController class which when i call [self.tableView reloadData]; it gets stuck in a loop? Is there a way to break the loop?
The UITableViewController calls another class which then calls a method in the UITableViewController{
-(void)callToConfirm
{
[arrMain removeAllObjects];
[arrMain addObject:[searchText capitalizedString]];
[self.tableView reloadData];
NSLog(#"Symbol Exists!");
}
Post the backtrace of the loop. Without more information, it is impossible to say, but there are two scenarios that can lead to this:
You are calling reloadData while filling the table view. If you have any kind of lazy loading mechanism and that mechanism is triggered while loading the table view and it calls reloadData, then... boom.
Your data load is telling the table view that it is invalidated and needs to reload during the next pass through the run loop. Effectively looks like an infinite loop.

UITableView Refresh Query

I'm using a UITableView control to show some data that can be edited by the user. To edit the details the user taps on an edit button which pushes a new view onto the stack. The user edits the data, taps a save button and the data is saved to a plist and the view popped off the stack. Even though the plist has been updated the UITableView still shows the old data. This can be corrected by adding a call to reloadData in the viewWillAppear method. However when the view is first loaded the data is displayed correctly, by adding the reload statement does this mean a double bind? If so, how can this be avoided?
I found the following code (here) which forces a refresh without explicitly calling reloadData:
- (void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
int orientation = [[UIDevice currentDevice] orientation];
if(orientation != UIDeviceOrientationUnknown)
[self willRotateToInterfaceOrientation:orientation duration:0];
}
Can anyone explain how/why this works?
The trick from your link is a dirty hack. Not only does it reload the data, but also forces the table to redraw. It tells your app that the device is getting a new orientation, so your table gets redrawn, along with other UI elements.
The standard way of refreshing a single row or a specific set of rows in your UITableView is calling its reloadRowsAtIndexPaths:withRowAnimation: method: doing so calls through to your data source to get data for only the row(s) that have been updated, preventing the full reload.
Do this:
- (void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
//remove all objects from yourTableViewDataSourceArray
[yourTableViewDataSourceArray removeAllObjects];
//add new records from plist
yourTableViewDataSourceArray = plist request of data here
//reload table now
[yourtableView reloadData];
}

Updating data for UITableView in background breaks animations

I have a UITableViewController which is supposed to fetch data in the background and then refresh the UITableView. However when I run the update method in the background it breaks all transition animations in the entire app (Slides when pushing view controllers on to the navigation stack). Weirdly, the exact same model works in other classes very similar to this one.
Here is the call I'm using for background updating:
[self performSelectorInBackground:#selector(updateData) withObject:nil];
However this works, but is of course not done in the background:
[self updateData];
And finally the method being run:
- (void)updateData{
updating = YES;
[progress show:YES];
dataSource = [[NetworkHandler sharedInstance:self] getRaces];
[progress hide:YES];
updating = NO;
[self.tableView reloadData];
}
The updating flag is not in any way an attempt at a semaphore, merely a way to ensure that the view doesn't get updated twice in case the user switches back and forth between views. ;)
[self.tableView reloadData]; looks like it could cause some kind of threading issue. All updates to the UI should be done in the main thread. So this should work:
[self.tableView performSelectorOnMainThread:#selector(reloadData)];
As of February 2015, the method mentioned in the answer above is deprecated, and replaced by:
[self.tableView performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:NO];

Insert rows in UITableView from code

This seems ridiculous but I can't seem to sort it out. I need to insert a row in a table view in response to a push notification. The implementation should be as simple as
- (void)didSaveMessage:(Message*)message atIndex:(int)index
{
//check to make sure I have an array to populate the table from...
if (self.messageManager.messages != nil)
{
self.indexRow = index;
NSLog(#"refresh table view from code");
//remove a label that says "You have no messages"...
[[messageTable tableFooterView] removeFromSuperview];
[messageTable beginUpdates];
[messageTable insertRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:index inSection:0]]
withRowAnimation:UITableViewRowAnimationFade];
[messageTable endUpdates];
[self scrollToNewestMessage];
}
}
This method is called when I receive a remote notification and it does fire. The log statement prints. However, the UITableView does not update. What is particularly strange to me is that if I put the same code in an IBAction and link it to a refresh button in storyboard, it works. Is there something special that I have to do to update a UITableView programmatically?
Try to send the table the reloadData message. This should force the table view to update.
It seems pretty trivial, but is it possible that the Message* that you are passing into the didSaveMessage is nil? It would explain why the NSLog statement works, and I would assume that you had to create the message manually when testing from an IBAction.
This is resolved. I was calling the method to update the UI from didReceiveRemoteNotification: in the AppDelegate but to a different instance of my view controller. So I added a viewController property onto the app delegate, set the value for that in my view controller's view did load, and now it's all working.