I have a tableView that periodically gets updated by data coming from a remote service. I load the data, and store the indexPaths of various data that I just inserted, deleted or updated. I then have a begin/endUpdates block that completes the necessary operations, as seen below.
[self.tableView beginUpdates];
if (pathsToDelete) {
[self.tableView deleteRowsAtIndexPaths:pathsToDelete withRowAnimation:UITableViewRowAnimationNone];
}
if (pathsToRefresh) {
[self.tableView reloadRowsAtIndexPaths:pathsToRefresh withRowAnimation:UITableViewRowAnimationNone];
}
[self.tableView insertRowsAtIndexPaths:indexPathsToAdd withRowAnimation:UITableViewRowAnimationNone];
[self.tableView endUpdates];
I noticed recently that cells were being reloaded way more than I was expecting. Following through in the debugger, I noted that in cases where a single record was being refreshed (ie pathsToRefresh was the only one that had anything, and it had only a single index) the entire table was actually reloading. By that, I mean I was seeing requests to the dataSource for each and every cell in the table.
In debugging, I started stripping things down. I got all the way to the point where I only had
[self.tableView beginUpdates];
[self.tableView endUpdates];
And even then, I was seeing the entire table reload (ie cellForRowAtIndexPath was fired for every value in the datasource). So I assumed something else was happening that I'd missed..but removing those two methods resulted in the reload stopping.
Shouldn't begin/endUpdates do nothing in cases where nothing is within the block? I'm at a loss on what is actually happening here, as the doc indicates all they do is bookend table operations - and in this case I have none.
objective c iOS reloadRowsAtIndexPaths , reloadSections
if([self.tableView numberOfRowsInSection:([self.tableView numberOfSections]-1)]-1 == -1){
row = 0;
}else{
row = [self.tableView numberOfRowsInSection:([self.tableView numberOfSections]-1)]-1 ;
}
NSIndexPath* indexPath = [NSIndexPath indexPathForRow: (row) inSection: ([self.tableView numberOfSections]-1)];
// [self.tableView reloadData];
[self.tableView beginUpdates];
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationAutomatic];
[self.tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationNone];
[self.tableView endUpdates];
NSLog(#”Number of Sections %ld”, (long)[self.tableView numberOfSections]);
NSLog(#”Number of Rows %ld”, [self.tableView numberOfRowsInSection:([self.tableView numberOfSections]-1)]-1);
Related
I have aUITableView with delegate methods declared. ThenumberOfRows &numberOfSections are updated dynamically.
(NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section{}
(NSInteger)numberOfSectionsInTableView:
Instead of using the [tableView reloadData], I would need to usereloadRowsAtIndexPath.
However I get a crash with invalid number of rows. How do I handle the dataSource changes? Lets says I have an array of count 10, and when I reload theUITableView usingUIRefreshControls by pulling, and the data from the server. From 10, if the count of array drops to 1, how will I handlereloadRowsAtIndexPath?
[self.tableView reloadRowsAtIndexPaths:[self.tableView indexPathForVisibleRows] withRowAnimation:UITableViewRowAnimationNone]
There seems to be a crash when this is called. How would I handle change in data array's count?
To reload specific cells of Tableview the method is reloadRowsAtIndexPaths: not reloadRowsAtIndexPath
[self.tableview reloadRowsAtIndexPaths:[self.tableview indexPathsForVisibleRows] withRowAnimation:UITableViewRowAnimationNone];
However for reloadRowsAtIndexPaths to work properly datasource needs to be consistant. As Indexpaths are already loaded.
If you still use this method, You will get either arrayIndexOutofBound error or duplicate values in cells(depending on increase or decrease in datasource).
//call begin and end updates while performing some hard core actions on tableview
[My_Table beginupdates];
[My_Table reloadRowsAtIndexPaths:[My_Table indexPathsForVisibleRows] withRowAnimation:UITableViewRowAnimationNone];
[My_Table endupdates];
hope it will helps you.
you can try with this.
[tblMe beginUpdates];
[tblMe reloadRowsAtIndexPaths:[NSArray arrayWithObjects:[NSIndexPath indexPathForRow:0 inSection:0], nil] withRowAnimation:UITableViewRowAnimationNone];
[tblMe endUpdates];
Hope this may help !!!
I have set the row insertion with the following code. I am only using UITableViewRowAnimationNone when inserting and deleting the rows, but sometimes ,as you can see in the gif below, the row animates in from the top or bottom. For the most part it doesn't animate, as I want it, but sometimes it animates on insertion and deletion. I am not talking about the table view expanding to show the inserted cell, I mean the cell appears to be sliding in from the bottom or top.
Here is the method that controls the insertion animation:
- (void)contentHeaderFooterView:(NFContentHeaderFooterView *)headerFooterView sectionOpened:(NSInteger)section{
NSIndexPath *pathToAdd = [NSIndexPath indexPathForRow:0 inSection:section];
[self.tableView beginUpdates];
[self.tableView insertRowsAtIndexPaths:#[pathToAdd] withRowAnimation:UITableViewRowAnimationNone];
[self.tableView endUpdates];
}
And here is the method that controls the deletion animation.
- (void)contentHeaderFooterView:(NFContentHeaderFooterView *)headerFooterView sectionClosed:(NSInteger)section{
NSIndexPath *pathToDelete = [NSIndexPath indexPathForRow:0 inSection:section];
[self.tableView beginUpdates];
[self.tableView deleteRowsAtIndexPaths:#[pathToDelete] withRowAnimation:UITableViewRowAnimationNone];
[self.tableView endUpdates];
}
I've had similar issues with table updates not performing the animation I gave it directly. I can't say for sure why, but one thing I can suggest is, instead of doing a specific row deletion between begin/endUpdates, you can just to a hard reloadData.
Answered here: https://stackoverflow.com/a/37352789/577237
Reposted since it's brief:
[UIView performWithoutAnimation:^{
[self.tableView beginUpdates];
[self.tableView deleteRowsAtIndexPaths:rowsToRemove withRowAnimation:UITableViewRowAnimationNone];
[self.tableView endUpdates];
}];
Note that this is iOS 7.0+
I have a table view with two rows in one section, i.e
---
Row A
Row B
If I want to animate row 1 into a new section, i.e. the end result being:
---
Row B
---
Row A
how should this be achieved? What I have tried already is:
[self.tableView beginUpdates];
[self.tableView deleteRowsAtIndexPaths:#[[NSIndexPath indexPathForRow: 0 inSection: 0]] withRowAnimation:UITableViewRowAnimationNone];
[self.tableView insertSections:[NSIndexSet indexSetWithIndex: 1] withRowAnimation:UITableViewRowAnimationNone];
[self.tableView endUpdates];
However, in iOS 8 at least, the row animates out but then the new section is un-rendered (i.e. white, no bottom row border) until some time after when something triggers a redraw and it comes back.
[self.tableView beginUpdates];
[self.tableView moveRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] toIndexPath:[NSIndexPath indexPathForRow:0 inSection:1]];
[self.tableView endUpdates];
Which raises an exception:
Invalid update: invalid number of sections. The number of sections contained in the table view after the update (2) must be equal to the number of sections contained in the table view before the update (1), plus or minus the number of sections inserted or deleted (0 inserted, 0 deleted).
And:
[self.tableView beginUpdates];
[self.tableView insertSections:[NSIndexSet indexSetWithIndex: 1] withRowAnimation:UITableViewRowAnimationNone];
[self.tableView moveRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] toIndexPath:[NSIndexPath indexPathForRow:0 inSection:1]];
[self.tableView endUpdates];
Which raises the exception:
cannot move a row into a newly inserted section (1)
Should I just not animate at all and reload the whole table?
You'll need to perform this in two steps.
First you'll need to add the section; you need to tell your data source to account for the new section. For a simple example like yours, you can just set a variable:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
if(hasTwoSections)
return 2;
return 1;
}
Then where you want to trigger the animation, call:
hasTwoSections = true; // this needs to be set before endUpdates
[self.tableView beginUpdates];
[self.tableView insertSections:[NSIndexSet indexSetWithIndex: 1] withRowAnimation:UITableViewRowAnimationAutomatic];
[self.tableView endUpdates];
At this point, the tableView will call the data source methods to update the table based on your changes.
Once the section has been added, you can move the row to it in another update block:
[self.tableView beginUpdates];
[self.tableView moveRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] toIndexPath:[NSIndexPath indexPathForRow:0 inSection:1]];
[self.tableView endUpdates];
Obviously you'll need to update what numberOfRowsInSection returns to account for the change.
Although it's two steps, it will look like it's happening all at once.
I was in a similar situation where I was getting the exception cannot move a row into a newly inserted section (1). I only found two half solutions.
Reload the table with reloadData() - aggressive, and no animations
Delete and insert your rows, code below:
self.requestTableView.deleteRowsAtIndexPaths([oldIndexPath], withRowAnimation: UITableViewRowAnimation.Automatic)
self.requestTableView.insertRowsAtIndexPaths([newIndexPath], withRowAnimation: UITableViewRowAnimation.Automatic)
To get animations effects that give the impression of the rows moving, you could use UITableViewRowAnimation.Top when deleting, inserting, or reloading sections.
self.tableView.deleteSections(toDelete, withRowAnimation: UITableViewRowAnimation.Top)
self.tableView.insertSections(toInsert, withRowAnimation: UITableViewRowAnimation.Top)
self.tableView.reloadSections(toReload, withRowAnimation: UITableViewRowAnimation.Top)
Use -[UITableView moveRowAtIndexPath:toIndexPath:]
When I reload a row using
[self.tableView beginUpdates];
[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
The UITableViewRowAnimationFade is ignored, and i get animation where the updated row slides in from the top. However this problems only exists if there is empty sections above the refreshed row, otherwise it's working fine.
Same problem if is removeRowsAtIndexPath and then insert it again before calling endUpdates.
I'm populating data using an array not NSFetchedResultsController.
Let's try remove begin Update and EndUpdate.
I change the height of a cell (when image downloaded) with method like that
-(void)didHeightChanged:(float)imageHeight atIndex:(NSInteger)indexPath
{
[dictionary setObject:imageHeight forKey:indexPath];
[self.tableView beginUpdates];
[self.tableView endUpdates];
}
Everything works well with first data, but when I reload UITableView with second data , probably beginUpdates method doesnt ended and I got issues - when I delete and reload UITableView, cell dont disappear, UITableView shows wrong cells
How can I stop beginUpdates immediately before reload new data?
You should either call [self.tableView reloadData] or [self.tableView reloadRowsAtIndexPaths:#[[NSIndexPath indexPathWithRow:index inSection:0]]]; // or indexPath if instead of passing a NSInteger you pass a NSIndexPath * to the method.
I found the answer
[self.tableView beginUpdates];
[self.tableView endUpdates];
it was called in background thread...