self.alphabets is my data source which contains all the alphabets.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{ [tableView beginUpdates];
if (editingStyle == UITableViewCellEditingStyleDelete) {
[self.alphabets removeObjectAtIndex:indexPath.row];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObjects:indexPath, nil] withRowAnimation:UITableViewRowAnimationFade];
}
if (editingStyle == UITableViewCellEditingStyleInsert) {
[self.alphabets insertObject:[self.alphabets objectAtIndex:indexPath.row] atIndex:[self.alphabets count]-1];
NSIndexPath * path1 = [NSIndexPath indexPathForRow:indexPath.row inSection:0];
NSArray * index = [NSArray arrayWithObjects:path1, nil];
[self.tableView insertRowsAtIndexPaths:index withRowAnimation:UITableViewRowAnimationAutomatic];
}
[tableView endUpdates];
NSLog(#"%#",self.alphabets);
}
This is the code, which i am using to add or insert rows/cells in tableview by using the data already present.
But i want to add or insert a new row/cell by taking the user input. How to do it.
I have tried to use UIAlertController. Is that a right approach? But i failed. Someone please help me.
Also, i have a doubt about inserting multiple rows at a time. How to achieve it.
Here is the code which i am using to insert multiple rows at a time.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView beginUpdates];
if (editingStyle == UITableViewCellEditingStyleInsert) {
[self.alphabets insertObject:[self.alphabets objectAtIndex:indexPath.row] atIndex:[ self.alphabets count]-1];
NSIndexPath * path1 = [NSIndexPath indexPathForRow:indexPath.row inSection:0];
NSIndexPath * path2 = [NSIndexPath indexPathForRow:indexPath.row+1 inSection:0];
NSArray * index = [NSArray arrayWithObjects:path1,path2, nil];
[self.tableView insertRowsAtIndexPaths:index withRowAnimation:UITableViewRowAnimationAutomatic];
}
[tableView endUpdates];
NSLog(#"%#",self.alphabets);
}
Here is the exception i am getting.
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (11) must be equal to the number of rows contained in that section before the update (10), plus or minus the number of rows inserted or deleted from that section (2 inserted, 0 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).'
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleInsert) {
UIAlertController * alert = [UIAlertController alertControllerWithTitle:#"Inserting a new row" message:#"Enter text below to add it to table view" preferredStyle:UIAlertControllerStyleAlert];
[alert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
}];
UIAlertAction * ok = [UIAlertAction actionWithTitle:#"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
NSArray * tfArray = alert.textFields;
UITextField * tf = [tfArray objectAtIndex:0];
self.userInput = tf.text;
NSLog(#"%#",self.userInput);
[self.alphabets insertObject:self.userInput atIndex:indexPath.row];
[tableView reloadData];
}];
[alert addAction:ok];
[self presentViewController:alert animated:NO completion:nil];
}
NSLog(#"%#",self.alphabets);
}
Using the above code, I am able to add or insert new rows or cells with user input.
I used a UIAlertController to complete this task. I added a UITextfield to the alert controller and can now get input from it. It in turn adds it to the datasource and the reloaded UITableview.
self.userInput is an string used to store the user input given through a textfield in an alert.
If anyone thinks that this code can be improved, then let me know. I am always willing to improve myself.
Related
I am developing an IOS app. Delete the row in tableview and data load in core data in table view. When Click the delete button App crash.
Reason is reason: Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (18) must be equal to the number of rows contained in that section before the update (18), plus or minus the number of rows inserted or deleted from that section (0 inserted, 1 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out)...
_fetchedobj is an NSArray to Fetch data from core data
//Code is
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return _fetchedobj.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *MyIdentifier = #"MyReuseIdentifier";
UITableViewCell *cell = [myTable dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier];
}
data = [_fetchedobj objectAtIndex:indexPath.row];
return cell;
}
-(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath*)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
[tableView beginUpdates];
NSManagedObject *managedObject = [_fetchedobj objectAtIndex:indexPath.row];
[self.managedObjectContext deleteObject:managedObject];
[myTable deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[myTable endUpdates];
}
}
Don't you forget to remove this object from your datasource array?
try repopulate your _fetchedobj because you must configure your model after deletion. For more detail, read https://developer.apple.com/library/prerelease/ios/documentation/UserExperience/Conceptual/TableView_iPhone/ManageInsertDeleteRow/ManageInsertDeleteRow.html
Try this
-(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath*)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
[tableView beginUpdates];
NSManagedObject *managedObject = [_fetchedobj objectAtIndex:indexPath.row];
[self.managedObjectContext deleteObject:managedObject];
// insert this line to your code
NSMutableArray *newA = [_fetchedobj mutableCopy ];
[newA removeObjectAtIndex: indexPath.row];
_fetchedobj = newA;
[myTable deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[myTable endUpdates];
}
}
Try using this line for deleting row of table view
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
This question already has answers here:
How to delete a row from UITableView
(5 answers)
Closed 9 years ago.
I want to delete a row from a tableView by using alertView message to confirm, but got some error output:
reason: 'Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (3) must be equal to the number of rows contained in that section before the update (3), plus or minus the number of rows inserted or deleted from that section (0 inserted, 1 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).'
Setup some of private member in SomeViewController interface
#interface SomeViewController ()
{
NSMutableArray *listItems;
NSString *itemId;
NSIndexPath *rowIndexPath;
}
In SomeViewController.m
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return [listItems count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [[listItems objectAtIndex:section] count];
}
#pragma mark - Table view operation
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
//add code here for when you hit delete
UIAlertView *deleteAlert = [[UIAlertView alloc] initWithTitle:#"Message"
message:#"Are you sure?"
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:#"Cancel", nil];
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
itemId = cell.textLabel.text;
rowIndexPath = indexPath;
deleteAlert.tag = 1;
[deleteAlert show];
}
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (alertView.tag == 1)
{
if (buttonIndex == 0) {
NSLog(#"itemId: %#", itemId);
NSLog(#"rowIndexPath: %#", rowIndexPath);
[self.tableView beginUpdates];
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:rowIndexPath]
withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
[self.tableView reloadData];
}
}
}
I can correctly set the tableView data and show deletion option, but can't figure it out why that error occurred.
Delete the datasource before you delete cell.
Hope it helps
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (alertView.tag == 1)
{
if (buttonIndex == 0) {
NSLog(#"itemId: %#", itemId);
NSLog(#"rowIndexPath: %#", rowIndexPath);
[self.tableView beginUpdates];
[[listItems objectAtIndex:section] removeObjectAtIndex:rowIndexPath.row]
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:rowIndexPath]
withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
[self.tableView reloadData];
}
}
Remember every time you do a delete action, you should always modify your data source first.
You can simply remove the object from the array, then reload the UITableView. Like So:
[YourArray removeObjectAtIndex:1]; //Change the index to which cell you want to "delete"
[YourTableView reloadData];
Or do as stated here: How to delete a row from UITableView.
I have an array (datasource) with a set of addresses, my tableview is set to have double the amount of cells from the datasource so I can style the odd cells to be small and clear (to make the tableview look like the cells are separated by a small space). My problem comes when deleting a row, I do the following:
-(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath{
[tableView beginUpdates];
if (editingStyle==UITableViewCellEditingStyleDelete) {
[self DeleteAddress:[ListAddress objectAtIndex:indexPath.row/2]];
[ListAddress removeObjectAtIndex: indexPath.row/2];
NSIndexPath *nextIndexPath = [[NSIndexPath alloc] initWithIndex:indexPath.row+1];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObjects:indexPath,nextIndexPath,nil] withRowAnimation:UITableViewRowAnimationRight];
}else if (editingStyle == UITableViewCellEditingStyleInsert){
[self tableView:tableView didSelectRowAtIndexPath:indexPath];
}
[tableView endUpdates];
}
The DeleteAddress method deletes the address from the database.
When the debugger reaches the [tableview endUpdate] function a get the following error:
*** Assertion failure in -[UITableView _endCellAnimationsWithContext:], /SourceCache/UIKit_Sim/UIKit-2380.17/UITableView.m:1070
NSInternalInconsistencyException
Just follow the sample code.Delete one row from your data source.Hope its useful for you.This code works for my app.Try it
// Swipe to delete has been used. Remove the table item
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete)
{
// Get a reference to the table item in our data array
Pictures *itemToDelete = [self.pictureListData objectAtIndex:indexPath.row];
// Delete the item in Core Data
[self.managedObjectContext deleteObject:itemToDelete];
// Remove the item from our array
[pictureListData removeObjectAtIndex:indexPath.row];
// Commit the deletion in core data
NSError *error;
if (![self.managedObjectContext save:&error])
NSLog(#"Failed to delete picture item with error: %#", [error domain]);
// Delete the row from the data source
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
}
My problem was that I was creating my nextIndexPath variable the wrong way. It should have been like this:
NSIndexPath *nextIndexPath = [NSIndexPath indexPathForRow:indexPath.row+1 inSection:indexPath.section];
Also I couldn't delete both rows at the same time, I needed to delete them separately from bottom:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:nextIndexPath] withRowAnimation:UITableViewRowAnimationRight];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationRight];
I am getting this error when deleting the last row in the section of a UITableView.
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '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 (3), plus or minus the number of sections inserted or deleted (0 inserted, 0 deleted).'
My table has 3 sections IF an array has a count above 0 (users can add locations themselves). If the array is equal to 0 then the table contains only 2 section, which is where I think the problem lies, I just can't figure it out.
The code I have for deleting the cells and updating the arrays is:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell* delCell = [tableView cellForRowAtIndexPath:indexPath];
delName = delCell.textLabel.text;
[self deleteLocation];
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
-(void)deleteLocation {
NSLog(#"Name = %#",delName);
NSUserDefaults *customLocations = [NSUserDefaults standardUserDefaults];
NSUserDefaults *sharedPref = [NSUserDefaults standardUserDefaults];
[customLocations removeObjectForKey:delName];
[customLocations synchronize];
[customLoc removeObject:delName];
saveCustomLoc = [NSArray arrayWithArray:customLoc];
[sharedPref setObject:saveCustomLoc forKey:#"CustomLocations"];
[sharedPref synchronize];
NSLog(#"%#",saveCustomLoc);
NSLog(#"%lu",(unsigned long)[saveCustomLoc count]);
[self showAlert];
}
- (void) showAlert {
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle:#"Location Deleted"
message:#"The location has been deleted"
delegate:nil
cancelButtonTitle:#"Ok"
otherButtonTitles:nil];
[alert show];
}
This works great until the final cell.
An example of how my table is set up is:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
{
if ([customLoc count] == 0) { // If there is no data in the custom locations array the don't include that section
if (section == 0) {
return [serverSelection count];
}
else {
return [qServerSelection count];
}
}
else {
if (section == 0) {
return [customLoc count];
}
else if (section == 1) {
return [serverSelection count];
}
else {
return [qServerSelection count];
}
}
So the headings/number of rows/number of sections etc. is dependent on the contents of an Array. The fact that the section the cells are being removed from disappears (or rather suddenly has 4 cells in as section 1 moves to section 0) I think is causing the issue.
Any ideas how to work around this?
After looking into this some more I found out (with the help of some Apple forum members) that when removing the last row from a section you also need to explicitly delete the section as well.
I also added table update code at the beginning and the end as shown below:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView beginUpdates];
UITableViewCell* delCell = [tableView cellForRowAtIndexPath:indexPath];
delName = delCell.textLabel.text;
[self deleteLocation];
if ([customLoc count] == 0) {
[tableView deleteSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:UITableViewRowAnimationTop];
}
else {
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
[tableView endUpdates];
}
So, if my array is 0 (in effect the last row is being deleted) the section itself is removed.
Note: when I tap the row, then the app crashes.
I'm trying to implement adding a new cell on a user's tap. I found that there was a similar example in WWDC 2011's table view demonstration. Here's my code from my table view.
Here is the error:
2013-03-19 20:04:28.672 Project[51229:c07] *** Assertion failure in -[UITableView _endCellAnimationsWithContext:], /SourceCache/UIKit_Sim/UIKit-2380.17/UITableView.m:1070
Here is my code from the table view.
#interface MyPFQueryTableViewController : PFQueryTableViewController <PFLogInViewControllerDelegate, PFSignUpViewControllerDelegate>
#property (nonatomic, strong) NSIndexPath *controlRowIndexPath;
#property (nonatomic, strong) NSIndexPath *tappedIndexPath;
#implementation MyPFQueryTableViewController {
ListItemObject *listDetail;
}
#synthesize controlRowIndexPath;
#synthesize tappedIndexPath;
-(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
PFObject *object = [self.objects objectAtIndex:indexPath.row];
[object deleteInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
[self loadObjects];
}];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object {
static NSString *CellIdentifier = #"listCell";
PFTableViewCell *cell = (PFTableViewCell *)[tableView dequeueReusableCellWithIdentifier:#"listCell"];
if (cell == nil) {
cell = [[PFTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Configure the cell
cell.textLabel.text = [object objectForKey:self.textKey];
//cell.imageView.file = [object objectForKey:self.imageKey];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[super tableView:tableView didSelectRowAtIndexPath:indexPath];
//if user tapped the same row twice let's start getting rid of the control cell
if([indexPath isEqual:self.tappedIndexPath]){
[tableView deselectRowAtIndexPath:indexPath animated:NO];
}
//update the indexpath if needed... I explain this below
indexPath = [self modelIndexPathforIndexPath:indexPath];
//pointer to delete the control cell
NSIndexPath *indexPathToDelete = self.controlRowIndexPath;
//if in fact I tapped the same row twice lets clear our tapping trackers
if([indexPath isEqual:self.tappedIndexPath]){
self.tappedIndexPath = nil;
self.controlRowIndexPath = nil;
}
//otherwise let's update them appropriately
else{
self.tappedIndexPath = indexPath; //the row the user just tapped.
//Now I set the location of where I need to add the dummy cell
self.controlRowIndexPath = [NSIndexPath indexPathForRow:indexPath.row + 1 inSection:indexPath.section];
}
//all logic is done, lets start updating the table
[tableView beginUpdates];
//lets delete the control cell, either the user tapped the same row twice or tapped another row
if(indexPathToDelete){
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPathToDelete]
withRowAnimation:UITableViewRowAnimationNone];
}
//lets add the new control cell in the right place
if(self.controlRowIndexPath){
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:self.controlRowIndexPath]
withRowAnimation:UITableViewRowAnimationNone];
}
//and we are done...
[tableView endUpdates];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if([indexPath isEqual:self.controlRowIndexPath]){
return 45; //height for control cell
}
return 70; //height for every other cell
}
- (NSIndexPath *)modelIndexPathforIndexPath:(NSIndexPath *)indexPath
{
int whereIsTheControlRow = self.controlRowIndexPath.row;
if(self.controlRowIndexPath != nil && indexPath.row > whereIsTheControlRow)
return [NSIndexPath indexPathForRow:indexPath.row - 1 inSection:0];
return indexPath;
}
#end
The problem is in your didSelectRowAtIndexPath method. You have:
[tableView beginUpdates];
//lets delete the control cell, either the user tapped the same row twice or tapped another row
if(indexPathToDelete){
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPathToDelete]
withRowAnimation:UITableViewRowAnimationNone];
}
//lets add the new control cell in the right place
if(self.controlRowIndexPath){
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:self.controlRowIndexPath]
withRowAnimation:UITableViewRowAnimationNone];
}
//and we are done...
[tableView endUpdates];
Before you make any calls to tell the table to add or remove any rows, you must update your data source with by adding or removing data. The table will check how many sections and rows there are before and after your add/remove rows. The number of sections and rows after the change must properly reflect how much data you add/remove with how many rows you add/remove.
And of course you must implement the numberOfRowsInSection method.
What does your - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section look like?
This error happens when you try to add or delete a row from the UITableView, but the number of rows that you claim to be in the section after the update in that method is not consistent with the new data that should be loaded.
Ex, if your numberOfRowsInSection always returns 4 and you add a row in that section, the tableView will want it to be 5, but it will not be so it will crash. You need to keep track of how many rows are in each section and return that number.