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+
Related
I am trying to create a chat bot like application, I have used a UITableView with custom cells to fit my needs. Whenever a new message is added, I create and insert a new row and then scroll to the bottom of the UITableView. Everything works fine till a certain point, but when the height of the cells change (I have two different type of cells), the animation is messy, it doesn't smoothly scroll to the end and the entire UITableView flashes, which is not a good user experience. I have tried a couple of approaches:
1 - Add the data to the data source array and reload the UITableView, then scroll to the bottom.
2 - Use insertRowsAtIndexPaths then scroll to the bottom.
Both of them have the same issue with scrolling. I have used scrollToRowAtIndexPath to get to the bottom of the UITableView
I have uploaded the code of a demo app that represents simulate the same issue here so it will be easy to understand. Here is a video of the issue. Any help is really appreciated.
This issue MAY NOT occur on a simulator, kindly run the demo project on a device.
After reading all the comments and having a discussion in chat, I noticed this is happening on the iPhone 5C (10.3.3). I ran the demo on an iPhone 5S (11.3) and the issue does not occur. Not sure if this has to do something with the OS.
Reloading whole tableview results in messy scrolling instead insert row at last position.
Make following changes in your Action Methods
- (IBAction)btnLargeCellClicked:(id)sender { // To add large green colored row.
/************ This is the FIRST approach. ***********/
[arrHeight addObject:#"100"];
[arrData addObject:#"1"];
NSIndexPath* ip = [NSIndexPath indexPathForRow:[_myTable numberOfRowsInSection:0] inSection:0];
[_myTable insertRowsAtIndexPaths:#[ip] withRowAnimation:UITableViewRowAnimationFade];
[self scrollTableviewToLastRow];
/************ This is the SECOND approach. ***********/
// [arrHeight addObject:#"100"];
// [_myTable beginUpdates];
// NSIndexPath *row1 = [NSIndexPath indexPathForRow:arrData.count inSection:0];
// [arrData insertObject:#"1" atIndex:arrData.count];
// [_myTable insertRowsAtIndexPaths:[NSArray arrayWithObjects:row1, nil] withRowAnimation:UITableViewRowAnimationFade];
// [_myTable endUpdates];
// [self scrollTableviewToLastRow];
}
And
- (IBAction)btnClicked:(id)sender { // To add small red colored row.
/************ This is the FIRST approach. ***********/
[arrHeight addObject:#"50"];
[arrData addObject:#"1"];
NSIndexPath* ip = [NSIndexPath indexPathForRow:[_myTable numberOfRowsInSection:0] inSection:0];
[_myTable insertRowsAtIndexPaths:#[ip] withRowAnimation:UITableViewRowAnimationFade];
[self scrollTableviewToLastRow];
/************ This is the SECOND approach. ***********/
// [arrHeight addObject:#"50"];
// [_myTable beginUpdates];
// NSIndexPath *row1 = [NSIndexPath indexPathForRow:arrData.count inSection:0];
// [arrData insertObject:#"1" atIndex:arrData.count];
// [_myTable insertRowsAtIndexPaths:[NSArray arrayWithObjects:row1, nil] withRowAnimation:UITableViewRowAnimationFade];
// [_myTable endUpdates];
// [self scrollTableviewToLastRow];
}
Hope this helps :)
After seeing your source, can say tableView reloadData reloads cells every time, that makes animation more busy every additional cell.
For your goals you should use another system:
[self.tableView beginUpdates];
[self.tableView reloadRowsAtIndexPaths:#[indexPathOfYourCell] withRowAnimation:UITableViewRowAnimationAutomatic];
[self.tableView endUpdates];
And than, maybe you will need scrollToRowAtIndexPath at the end too
You would need this, change second approach like below
[arrHeight addObject:#"50"];
// [_myTable beginUpdates];
NSIndexPath *row1 = [NSIndexPath indexPathForRow:arrData.count inSection:0];
[arrData insertObject:#"1" atIndex:arrData.count];
[_myTable insertRowsAtIndexPaths:[NSArray arrayWithObjects:row1, nil] withRowAnimation:UITableViewRowAnimationNone];
[_myTable reloadRowsAtIndexPaths:#[row1] withRowAnimation:UITableViewRowAnimationNone];
// [_myTable endUpdates];
[self scrollTableviewToLastRow];
I am trying to delete row by using following code
[myTable beginUpdates];
[myTable deleteRowsAtIndexPaths:[NSArray arrayWithObjects:indexPath, nil] withRowAnimation:UITableViewRowAnimationBottom];
[myTable endUpdates];
Animation is working fine for all rows except for first row,
Please let me know reasons/solution if any
Thanks
[CATransaction begin];
[CATransaction setCompletionBlock:^{
// animation has finished
NSLog(#"My Animation Ended "); //Check if this is printed when zero index is deleted.
}];
[myTable beginUpdates];
[myTable deleteRowsAtIndexPaths:[NSArray arrayWithObjects:indexPath, nil] withRowAnimation:UITableViewRowAnimationBottom];
[myTable endUpdates];
[CATransaction commit];
If it is printed, means there was animation, so we could conclude why its not showing.
If there is only 1 row in the section then you shouldn't be deleting that row, you should be deleting the section.
Note that in all cases you also need to update the data store to remove the row / section at the same time that you update the table view with the change.
The issue is resolved,
The problem was i was setting alpha of table to zero immediately after calling delete animation as shown below, anyway thanks for your replies.
[myTable beginUpdates];
[myTable deleteRowsAtIndexPaths:[NSArray arrayWithObjects:indexPath, nil] withRowAnimation:UITableViewRowAnimationBottom];
[myTable endUpdates];
myTable.alpha = 0;
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);
I have a tableview with (say) five rows in the only section. The code to edit a custom cell all works, but the problem occurs when I need to reload the table data with new data that has fewer rows, say two. The programme crashes if I have been editing a row that has a higher index than the maximum number of rows in the reloaded table. The exception shows that it is trying to access the higher numbered element beyond the limit of the new table number of rows.
I have tried putting:
[self.tableView selectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] animated:YES scrollPosition:UITableViewScrollPositionTop];
in just before the
[self.tableView reloadData];
but it doesn't work.
It can only mean that you are accessing the first row here:
[self.tableView selectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] animated:YES scrollPosition:UITableViewScrollPositionTop];
when you do not have the first row.
When you are reloading the tableview, make sure you have atleast one row.
Try doing this:
if([self.tableview numberOfRowsInSection:0]>0)
[self.tableView selectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] animated:YES scrollPosition:UITableViewScrollPositionTop];
I believe you used the code below to unselect your cell
[self.tableView selectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] animated:YES scrollPosition:UITableViewScrollPositionTop];
If so you should use this instead
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
[self.tableView deselectRowAtIndexPath:indexPath animated:NO];
In the end, deselecting the old row (i.e. whilst the old data source was still current) and then selecting the first row with the new data source fixed the problem.
In my UITableView instance, I'm moving the cell to index:0 and reloading the data. The problem is that the cell "snaps" to the top (due to the reloadData, i think). I'm wondering if i can move the cell up, then apply reloadData a few seconds later. Heres my code:
[_toDoItems removeObject:todoItem];
[_toDoItems insertObject:todoItem atIndex:0 ];
[self.tableView beginUpdates];
[self.tableView moveRowAtIndexPath:origIndexPath toIndexPath:0];
[self.tableView endUpdates];
sleep(1);
[self.tableView reloadData];
the problem with this is, after the method is called, everything pauses for 1 second, then the "snap" animation occurs.
You first tell the tableView to expect updates with beginUpdates. Then update the relevant sections (if your tableView has only one section then just pass zero for the section number). Here's where you specify the animation - you can play around with it to get the effect that you want. After that you call endUpdates on the tableView. The typedef for UITableViewRowAnimation specifies:
typedef enum {
UITableViewRowAnimationFade,
UITableViewRowAnimationRight,
UITableViewRowAnimationLeft,
UITableViewRowAnimationTop,
UITableViewRowAnimationBottom,
UITableViewRowAnimationNone,
UITableViewRowAnimationMiddle,
UITableViewRowAnimationAutomatic = 100
} UITableViewRowAnimation;
Play around to see which one you want. Even selecting UITableViewRowAnimationNonecan have a nice effect sometimes. Depending on how many sections you have you can use - (void)reloadSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation
Table update code below:
[self.tableView beginUpdates];
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationNone];
[self.tableView endUpdates];
Solved.
[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(briefPause) userInfo:nil repeats:NO];
I used the above in the same method as
[self.tableView beginUpdates]
....
but I made a new function, briefPause which is just:
[self.tableView reloadData];
Try this:
[self.tableView beginUpdates];
[self.tableView deleteRowsAtIndexPaths:#[[NSIndexPath indexPathForRow:origIndexPath
inSection:0]]
withRowAnimation:UITableViewRowAnimationAutomatic];
[self.tableView insertRowsAtIndexPaths:#[[NSIndexPath indexPathForRow:0
inSection:0]]
withRowAnimation:UITableViewRowAnimationAutomatic];
[self.tableView endUpdates];