Error of adding row into UITableView with animation - ios

I use this to add rows to my UITableView with animation :
[bookMarksArray insertObject:infoDict atIndex:0];
NSIndexPath *newIndexPath = [NSIndexPath indexPathForRow:[bookMarksArray count] inSection:0];
[bookMarkTable insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
when i call [bookMarkTable reloadData] all works fine. But if i try to use previous code my app crashes with error : attempt to insert row 1 into section 0, but there are only 1 rows in section 0 after the update . Any ideas ?

#Jonathan's explanation for the exception is correct. But the solution is still wrong. You've inserted an object at index 0, so the index path of the inserted row would be:
NSIndexPath *newIndexPath = [NSIndexPath indexPathForRow:0 inSection:0];
If you had inserted the object at the end of the array, you would do
NSIndexPath *newIndexPath = [NSIndexPath indexPathForRow:[bookMarksArray count] - 1 inSection:0];

You need [bookMarksArray count] - 1 when creating the index path (its 0 indexed).

Try to update numberOfRows before call insertRowsAtIndexPath.

Related

Insert last row in UITableView with smooth scrolling

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];

Select the row after reloaddata is complete on UITableView

I have a UITableView with custom UITableViewCell and datasource.
My tableviewdelegate class is registered to callback on on an event and once I got the event, I am reloading the data on the tableview.
The call back comes after I edit the tableviewcell data. But once the callback came, when I reloaddata, the tableview is scrolling to the top; and not staying at the current selection.
So I have cached the current selected cell and trying to scrollToRowAtIndexPath.
But I am hitting a crash.
How can I scroll to that row after tabledata is loaded ?
{
[m_tableView reloadData];
if(m_currentEditingCell != nullptr)
{
[m_tableView scrollToRowAtIndexPath:m_currentEditingCell atScrollPosition:UITableViewScrollPositionNone animated:YES];
}
}
You should provide NSIndexPath instead of providing your tableviewcell as argument for the selector scrollToRowAtIndexPath:atScrollPosition: animated:
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
[yourTableView scrollToRowAtIndexPath:indexPath
atScrollPosition:UITableViewScrollPositionTop
animated:YES];
The following code is able to select the cell after reload.
{
[m_tableView reloadData]; dispatch_async(dispatch_get_main_queue(), ^{ NSIndexPath* indexPath = [NSIndexPath indexPathForRow: ([m_tableView numberOfRowsInSection:([m_tableView numberOfSections]-1)]-1) inSection:([m_tableView numberOfSections]-1)]; if(m_currentCell != nullptr ) [self selectCurrentCell:m_currentCell];
}
}

after add row, UITableView add duplicate row

When I add a new row in my UITableView, it adds a duplicate row. The first row works perfectly, the second row works perfectly, but the third row is a duplicate of the second row.
Why is this?
Here is my code:
- (void)addExerciseToCustomTable
{
// save my parametr from DB
[self saveExerciseInCacheTableApproach:_approach andRepeat:_repeat andWeight:_weight andTimer:_timer andIndex:_index];
//update position in my TableView from db( 1....10)
[self updatePositionInCustomViewForAddRow:[NSString stringWithFormat:#"%d", ([_exerciseForGroupWithSelect count] - 1) ]];
_exerciseForGroupWithSelect = [self loadExerciseFromExerciseCacheWithGroup:groupExerciseId];
//load new array after update position
[_customExerciseTable beginUpdates];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:([_exerciseForGroupWithSelect count] - 1) inSection:0];
[_customExerciseTable insertRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationLeft];
[_customExerciseTable endUpdates];
}
solved this problem - after edit my table i update my DB
now - i load my nutablearray and work with him, after edit complete, save array in DB
it's work perfect

How to dynamically add rows to a specific UITableView section?

I am a new IOS Programmer, and i am having a issue.
I have a UITableView with 2 sections, one then is static, and another one is dynamical.
In specific actions i need to add new rows for the second section in runtime..
I know how to manage a UITableView , but not a specific section
Could you help me please?
Best Regards you all
You can use insertRowsAtIndexPaths: method of UITableView
//Update data source with the object that you need to add
[tableDataSource addObject:newObject];
NSInteger row = //specify a row where you need to add new row
NSInteger section = //specify the section where the new row to be added,
//section = 1 here since you need to add row at second section
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section];
[self.tableView beginUpdates];
[self.tableView insertRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationRight];
[self.tableView endUpdates];
You can use the same method insertRowAtIndexPath like
// Add the items into your datasource object first. Other wise you will end up with error
// Manage the number of items in section. Then do
NSIndexPath *indexPath1 = [NSIndexPath indexPathForRow:0 inSection:1];
NSIndexPath *indexPath2 = [NSIndexPath indexPathForRow:1 inSection:1];
[self.tableView insertRowsAtIndexPaths:#[indexPath1,indexPath2] withRowAnimation:UITableViewRowAnimationTop];
This will insert two rows to the section1. Remember before doing this you have manage your datasource object.
Swift 3 version
// Adding new item to your data source
dataSource.append(item)
// Appending new item to table view
yourTableView.beginUpdates()
// Creating indexpath for the new item
let indexPath = IndexPath(row: dataSource.count - 1, section: yourSection)
// Inserting new row, automatic will let iOS to use appropriate animation
yourTableView.insertRows(at: [indexPath], with: .automatic)
yourTableView.endUpdates()
you can do this by using scrolltoinfinite method but before that u have to import 3rd party svpulltorefresh
[self.tableViewMixedFeed addInfiniteScrollingWithActionHandler:^{
CGFloat height = self.tableViewMixedFeed.frame.size.height;
CGFloat contentYoffset = self.tableViewMixedFeed.contentOffset.y;
CGFloat distanceFromBottom = self.tableViewMixedFeed.contentSize.height - contentYoffset;
if(distanceFromBottom<contentYoffSet)
{
[weak insertRowAtBottom];
}
}];
-(void)insertRowAtBottom
{
[self.tableViewMixedFeed beginUpdates];
[self.tableViewMixedFeed insertRowsAtIndexPaths:indexPaths1 withRowAnimation:UITableViewRowAnimationTop];
[self.tableViewMixedFeed endUpdates];
[self.tableViewMixedFeed.infiniteScrollingView stopAnimating];
}
here indexpaths1 is the array of indexpaths of next cells which u want to insert in a table .
try using loop to get the next set of arrays and store them into indexpaths1.
[self.tableView insertRowsAtIndexPaths:#[indexPath1,indexPath2] withRowAnimation:UITableViewRowAnimationTop];
USE THIS METHOD..

ios using moveRowAtIndexPath:toIndexPath: correctly

Okay so I'm making a to do list app. I'm just wondering how to use moveRowAtIndexPath:toIndexPath: properly since it keeps crashing if the toDoItemCompleted method is triggered. I'm trying to move a row down to the bottom of the list once the method is triggered.
-(void)toDoItemCompleted:(ToDoItem *)todoItem {
NSUInteger origIndex = [_toDoItems indexOfObject:todoItem];
NSIndexPath *origIndexPath = [[NSIndexPath alloc]initWithIndex:origIndex];
NSUInteger endIndex = _toDoItems.count-1;
NSIndexPath *endIndexPath = [[NSIndexPath alloc]initWithIndex:endIndex];
[self.tableView beginUpdates];
[self.tableView moveRowAtIndexPath:origIndexPath toIndexPath:endIndexPath];
[self.tableView endUpdates];
}
You don't say what the error is. You should post the full error and point out which line of code is actually causing the error.
But one issue with your code is that you forgot to update your data source. This needs to be done before updating the table view.
Another issue is with how you create the index paths.
Something like this:
- (void)toDoItemCompleted:(ToDoItem *)todoItem {
NSUInteger origIndex = [_toDoItems indexOfObject:todoItem];
NSIndexPath *origIndexPath = [NSIndexPath indexPathForRow:origIndex inSection:0];
NSUInteger endIndex = _toDoItems.count - 1;
NSIndexPath *endIndexPath = [NSIndexPath indexPathForRow:endIndex inSection:0];
// Update date source
[_toDoItems removeObject:todoItem]; // remove from current location
[_toDoItems addObject:todoItem]; // add it to the end of the list
[self.tableView beginUpdates];
[self.tableView moveRowAtIndexPath:origIndexPath toIndexPath:endIndexPath];
[self.tableView endUpdates];
}

Resources