iOS NSMutableArray insertObject index out of bound - objective-c

I'm new to Objective-C programming so sorry for the lame question.
I'm trying to write some simple app that would add single numbers stored in the NSMutableArray to the table view.
Here is the init code and code for addItem() method:
- (void)viewDidLoad {
[super viewDidLoad];
self.title = #"Test App";
self.navigationItem.leftBarButtonItem = self.editButtonItem;
addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:#selector(addItem)];
self.navigationItem.rightBarButtonItem = addButton;
self.itemArray = [[NSMutableArray alloc] initWithCapacity:100];
}
- (void)addItem {
[itemArray insertObject:#"1" atIndex:0];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
This line
[itemArray insertObject:#"1" atIndex:0];
produces following error:
*** -[NSMutableArray objectAtIndex:]: index 0 beyond bounds for empty array'
Why index 0 is beyond bounds for empty array and what I'm doing wrong???
UPD
As BP pointed out, insertion into array might not work for empty array, so I added following check:
if ([itemArray count] == 0) {
[itemArray addObject:#"1"];
} else {
[itemArray insertObject:#"1" atIndex:0];
}
So now I get same error message, but at line
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
Any ideas on what might be wrong with it?

The call you make in insertRowsAtIndexPaths:withRowAnimation: should be between a beginUpdates and endUpdates call on your tableView.
Check the TableView Programming Guide.

The initWithCapacity method only sets aside memory for the objects, it does not actually create the number of objects specified in your array.
I am not sure if you can insert into an array that has no objects in it yet, so you might want to try to use the addObject method if the array count is 0, and the insertObject method otherwise.

Related

UITableView deleteRowsAtIndex & datasource troubles

Happy 14th Baktun everybody!
This thing has been bugging me for a while, and I've googled to and fro, and also checked SO for an answer, but I just can't seem to grasp the answers and apply it to my app. If some of you would be so kind to help out, that would be appreciated!
I got the following function that deletes a cell from an UITableView when a stepper reaches 0.
I want the cell to be animated to the left, so when you press the - sign you'll see the cell swipe out of view. I set the sender.tag to the [[cell stepper]setTag:indexPath.row]; in CellForRowAtIndexPath.
- (void)stepperChanged:(UIStepper *)sender
{
WinkelWagen *ww = [WinkelWagen sharedWinkelWagen];
BWBand *band = [ww.winkelWagenArray objectAtIndex: sender.tag];
NSDecimalNumber *nummer = [[NSDecimalNumber alloc]initWithFloat:sender.value];
band.winkelmandjeAantal = nummer;
[sender setValue:[band.winkelmandjeAantal doubleValue]];
// Remove the cell and object from WinkelWagen if stepper.value turns 0
if (sender.value == 0)
{
NSArray *deleteIndexPaths = [NSArray arrayWithObjects:
[NSIndexPath indexPathForRow:sender.tag inSection:0],
nil];
[ww.winkelWagenArray removeObject:band];
[self.tableView beginUpdates];
[self.tableView reloadData];
[self.tableView deleteRowsAtIndexPaths:deleteIndexPaths withRowAnimation:UITableViewRowAnimationNone];
if ([winkelmandjeData count] == 0)
{
NSArray *addIndexPaths = [NSArray arrayWithObjects:
[NSIndexPath indexPathForRow:0 inSection:0],
nil];
[self.tableView insertRowsAtIndexPaths:addIndexPaths withRowAnimation:UITableViewRowAnimationFade];
}
[self.tableView endUpdates];
}
}
UPDATE:
I've worked it out with [[self.tableview]reloaddata] but then the animation doesn't play. Can someone tell me how I should handle the data and the animation properly?
Thanks!
I see there something you could have a problem with, but don't know if it is an answer to your question. In your if ([shoppingCartData count] == 0) you are adding a row to the table, but the data source remains the same. That could result in a crash.
Also, before the if, your data source seem to be ww.shoppingCartArray, but in the if you are checking the count of shoppingCartData.
Hope this helps!

NSInternalInconsistencyException', reason: 'attempt to insert row 0 into section 0, but there are only 0 rows in section 0 after the update'

I am using UITableViewController and getting this error while updating tableView. Below is my code:
This occurs when i do a click event:
[timeZoneNames insertObject:#"HELLO" atIndex:0];
[self.tableView beginUpdates];
NSArray *insertIndexPaths = [NSArray arrayWithObject:[NSIndexPath indexPathForRow:0 inSection:0]];
[self.tableView insertRowsAtIndexPaths:insertIndexPaths withRowAnimation:UITableViewRowAnimationTop];
[self.tableView endUpdates];
I tried looking for apple documentation but that didnt helped.
Thanks,
Aby
I ran into this error when I forgot to set the datasource outlet properly.
If you see this error, check that you have explicitly set your TableView delegate and datasource :
go to your interface builder and look at the view with the 'Table View'
cmd + right click drag ( you should see a blue line ) from the 'Table View' icon to the title icon for the file, this has a yellow icon next to it in xcode 6.
release your mouse , you should see delegate and datasource options.
select 'datasource'
your table should now be correctly wired.
I've ran into this problem before. It means that when -insertRowsAtIndexPaths:withRowAnimation: was called -tableView:numberOfRowsInSection: returned 0. You need to insert into the model before you call -insertRowsAtIndexPaths:withRowAnimation: (see: -beginUpdates)
Update
I wonder what the return value of -tableView:numberOfRowsInSection: is? Also, you don't need -beginUpdates/-endUpdates if you only have one update.
[timeZoneNames insertObject:#"HELLO" atIndex:0];
// Let's see what the tableView claims is the the number of rows.
NSLog(#"numberOfRowsInSection: %d", [self tableView:self.tableView numberOfRowsInSection:0]);
NSArray *insertIndexPaths = [NSArray arrayWithObject:[NSIndexPath indexPathForRow:0 inSection:0]];
[self.tableView insertRowsAtIndexPaths:insertIndexPaths withRowAnimation:UITableViewRowAnimationTop];
I tried printing out how many rows and sections were actually in the tableView during the update which got me thinking, how many sections am I returning in the table view data source method...and this was the culprit:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 0;
}
Make sure you are returning 1 and not 0.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
At least this solved the problem for me...
UPDATE:
I'm having the same problem again after it was working for a while. I had modified the app a bit. My first view is a tableView and when you click the plus button on the upper right, you get another table where you can input information. When you click done, the information is added to the Core Data model with this code:
- (IBAction)doneButtonPressed:(UIBarButtonItem *)sender
{
MoneyBadgeAppDelegate *appDelegate =
[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context =
[appDelegate managedObjectContext];
NSManagedObject *newMoment;
newMoment = [NSEntityDescription
insertNewObjectForEntityForName:#"SpendingMoment"
inManagedObjectContext:context];
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"yyyyMMdd"];
NSDate *modifiedDate = [dateFormat dateFromString: self.dateTextField.text];
[newMoment setValue: modifiedDate forKey:#"date"];
[newMoment setValue: descTextField.text forKey:#"desc"];
[appDelegate.eventsArray insertObject:newMoment atIndex:0];
NSError *error;
[context save:&error];
[self dismissViewControllerAnimated:YES completion:nil];
}
And I confirmed it gets put into the Core Data model successfully by printing to the console the following in my viewDidAppear method upon returning back to the first screen.
-(void)viewDidAppear:(BOOL)animated{
NSEntityDescription *entity = [NSEntityDescription entityForName:#"SpendingMoment"
inManagedObjectContext:self.managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:entity];
NSError *error;
NSArray *items = [self.managedObjectContext
executeFetchRequest:fetchRequest error:&error];
for (SpendingMoment *moment in items) {
NSLog(#"Date: %#, Desc: %#", [moment date], [moment desc]);
}
[self addEvent];
And then my addEvent method does this:
- (void)addEvent {
[self.tableScreen beginUpdates];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
[self.tableScreen insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
[self.tableScreen reloadData];
[self.tableScreen endUpdates];
#try{
[self.tableScreen scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:YES];
}
#catch(NSException *exception){
}
}
Any idea what the problem is this time??? Thanks!
Check this "attempt to insert row 0 into section 0, but there are only 0 rows in section 0 after the update".
It mesns when you insert object in row 0 , section 0, there are no any sections.
Try to insert section before you insert row.
[timeZoneNames insertObject:#"HELLO" atIndex:0];
NSLog(#"sections: %lu ",[self.downlaodTableView numberOfSections];
// insert section
[self.tableView insertSections:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, 1)] withRowAnimation:UITableViewRowAnimationNone];
NSLog(#"numberOfRowsInSection: %d", [self tableView:self.tableView numberOfRowsInSection:0]);
NSArray *insertIndexPaths = [NSArray arrayWithObject:[NSIndexPath indexPathForRow:0 inSection:0]];
[self.tableView insertRowsAtIndexPaths:insertIndexPaths withRowAnimation:UITableViewRowAnimationTop];

Updating NSMutableArray from sectioned tableview after item is deleted

I have a tableview which is sectioned alphabetically. I have an NSMutableArray *drinks which is my original datasource which its being populated with NSMutableDictionary's from a plist. in my viewDidLoad method. I then iterate through my array and create the keys for the sections and the number of rows in each section in my viewWillAppear method
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
self.sections = [[NSMutableDictionary alloc] init];
BOOL found;
// Loop through the whiskys and create our keys
for (NSMutableDictionary *whisky in self.drinks)
{
NSString *c = [[whisky objectForKey:NAME_KEY] substringToIndex:1];
found = NO;
for (NSString *str in [self.sections allKeys])
{
if ([str isEqualToString:c])
{
found = YES;
}
}
if (!found)
{
[self.sections setValue:[[NSMutableArray alloc] init] forKey:c];
}
}
// Loop again and sort the whiskys into their respective keys
for (NSDictionary *whisky in self.drinks)
{
[[self.sections objectForKey:[[whisky objectForKey:NAME_KEY] substringToIndex:1]] addObject:whisky];
}
// Sort each section array
for (NSString *key in [self.sections allKeys])
{
[[self.sections objectForKey:key] sortUsingDescriptors:[NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:NAME_KEY ascending:YES]]];
}
[self.tableView reloadData];
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view
NSArray *searchPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [searchPaths lastObject];
NSString *writeableDBPath = [documentsDirectory stringByAppendingPathComponent:#"ScotchList.plist"];
NSMutableArray *tmpArray = [[NSMutableArray alloc]initWithContentsOfFile:writeableDBPath];
self.drinks = tmpArray;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationDidEnterBackground:)
name:UIApplicationDidEnterBackgroundNotification
object:nil];
//Register for application exiting information so we can save data
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(applicationWillTerminate:) name:UIApplicationWillTerminateNotification object:nil];
}
All of this works fine and dandy. Then when I delete a row it works fine as well. How ever when I go to a new view it and come back to the tableview the deleted row reappears. So I need to update my original datasource (drinks) because everytime the viewAppears it pulls from the original datasource again. So I tried to get the item (whisky) at the row and remove it from the array. But it throws a sigabrt. This is my delete row method.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
//DELETE THE ROW FROM THE DATA SOURCE
[self.tableView beginUpdates];
[[self.sections valueForKey:[[[self.sections allKeys] sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)] objectAtIndex:indexPath.section]] removeObjectAtIndex:indexPath.row];
NSDictionary *whisky = [[self.sections valueForKey:[[[self.sections allKeys] sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)] objectAtIndex:indexPath.section]] objectAtIndex:indexPath.row];
[self.drinks removeObject:whisky];
if ([[self.sections valueForKey:[[[self.sections allKeys] sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)] objectAtIndex:indexPath.section]] count] > 0)
{
// Section is not yet empty, so delete only the current row.
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
else
{
// Section is now completely empty, so delete the entire section.
[self.sections removeObjectForKey:[[[self.sections allKeys] sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)] objectAtIndex:indexPath.section]];
[tableView deleteSections:[NSIndexSet indexSetWithIndex:indexPath.section]
withRowAnimation:UITableViewRowAnimationFade];
}
[self.tableView endUpdates];
} else if (editingStyle == UITableViewCellEditingStyleInsert) {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view.
}
}
this is what the log shows:
2012-07-06 15:36:42.454 WhiskyBook[3275:907] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayI objectAtIndex:]: index 1 beyond bounds [0 .. 0]'
*** First throw call stack:
(0x3998a17b 0x3494395b 0x398d83bd 0x3e6b5 0x32a0cd11 0x328f0d09 0x328f0cbb 0x328f0c95 0x328f09eb 0x328f1369 0x328ef791 0x328dd72d 0x328dd009 0x339a7603 0x339a7233 0x3995a873 0x3995a817 0x39959611 0x398d6be5 0x398d6a71 0x339a634b 0x329037f5 0x3ac09 0x33cc7b20)
libc++abi.dylib: terminate called throwing an exception
(lldb)
What am I doing wrong?
I found a solution to my problem. Instead of trying to remove the object from the original datasource inside of the commitEditingStyle method I just made an NSDictionary property for my master-viewController alloc'd and inited it in my viewDidLoad then assigned it the item that I want to be deleted inside of the commitEditingStyle method. Then inside of my viewWillDisappear method I removed it from the original datasource.

Reload TableView data after click button

I'm making chat application and read messages data from SQLite database. I want to reload my TableView data with new messages after clicking on my "send" button.
Event for my button:
[_sendButton addTarget:self action:#selector(saveMessage:) forControlEvents:UIControlEventTouchUpInside];
I think I need to use -(void)viewDidAppear:(BOOL)animated but it didn't work (or I'm doing something wrong).
[_tableView reloadData];
-(void)viewDidAppear:(BOOL)animated {
sqlite3 *database;
databaseName = #"Messenges.db";
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
databasePath = [documentsDir stringByAppendingPathComponent:databaseName];
messenges = [[NSMutableArray alloc] init];
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK) {
const char *sqlStatement = "select * from messenges";
sqlite3_stmt *compiledStatement;
if(sqlite3_prepare_v2(database, sqlStatement, -1, &compiledStatement, NULL) == SQLITE_OK) {
while(sqlite3_step(compiledStatement) == SQLITE_ROW) {
NSString *dbMessageText = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 1)];
Message *messege = [[Message alloc] initWithName:dbMessageText];
[messenges addObject:messege];
[messege release];
}
}
sqlite3_finalize(compiledStatement);
}
sqlite3_close(database);}
Editing
This is my method to add a new row to TableView. New row must be added at once after clicking "send" button.
-(IBAction)insertRows:(id)sender {
MDAppDelegate *appDelegate = (MDAppDelegate *)[[UIApplication sharedApplication] delegate];
[messenges insertObject:[[NSString alloc] initWithFormat:#"%#", _textField.text] atIndex:appDelegate.messenges.count+1];
NSArray *insertIndexPaths = [NSArray arrayWithObject: [NSIndexPath indexPathForRow:1 inSection:1]];
UITableView *tV = (UITableView *)self.tableView;
[tV beginUpdates];
[tV insertRowsAtIndexPaths:insertIndexPaths withRowAnimation:UITableViewRowAnimationRight];
[tV endUpdates];}
But this code give me error: 'Invalid table view update. The application has requested an update to the table view that is inconsistent with the state provided by the data source.'
How can I fix it?
Edit2
Ok. I have only 1 section. numberOfSectionsInTableView: does not require correction.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
MDAppDelegate *appDelegate = (MDAppDelegate *)[[UIApplication sharedApplication] delegate];
NSUInteger numberOfRowsInSection = appDelegate.messenges.count; //add new variable to what rows will be +1
if (self.editing) {
numberOfRowsInSection++;
}
return numberOfRowsInSection;}
But I still have the same error.
Edit #3
Let's look. I changed my insertRows to :
-(IBAction)insertRows:(id)sender {
MDAppDelegate *appDelegate = (MDAppDelegate *)[[UIApplication sharedApplication] delegate];
[self.tableView beginUpdates];
self.tableView.editing = YES;
[messenges insertObject:[[NSString alloc] initWithFormat:#"%#", _textField.text] atIndex:appDelegate.messenges.count+1];
NSArray *insertIndexPaths = [NSArray arrayWithObject: [NSIndexPath indexPathForRow:appDelegate.messenges.count+1 inSection:1]];
[self.tableView insertRowsAtIndexPaths:insertIndexPaths withRowAnimation:UITableViewRowAnimationRight];
[self.tableView endUpdates];
}
And numberOfRowsInSection:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
MDAppDelegate *appDelegate = (MDAppDelegate *)[[UIApplication sharedApplication] delegate];
NSUInteger numberOfRowsInSection = appDelegate.messenges.count;
if (self.tableView.editing) {
numberOfRowsInSection++;
}
NSLog(#"%i",numberOfRowsInSection);
return numberOfRowsInSection;
}
But I got an error *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 4 beyond bounds [0 .. 3]' . Then I corrected if (self.tableView.editing) to:
if (self.tableView.editing) {
MDAppDelegate *someNewDelegate = (MDAppDelegate *)[[UIApplication sharedApplication] delegate];
numberOfRowsInSection = someNewDelegate.messenges.count+1; //or .count
}
But got same error about 'NSRangeException'.
To update your UITableView with animation you need to:
Invoke beginUpdates on your UITableView
Update your data model
Insert / Remove rows and sections
Invoke endUpdates on your UITableView
Couple of Things to Keep in Mind:
If you are deleting a section you should not also be deleting rows from that section (unnecessary and might even cause errors)
If you are inserting a section you should not also be inserting rows into that section (UITableView will call numberOfRowsInSection: followed by cellForRowAtIndexPath: if the cell is visible).
What Do You Mean, Update My Data Model?
If you are manipulating a UITableView by adding and removing rows and sections, then you should have some way of keeping track of what sections / rows are currently in the UITableView at any given moment.
For example, you should have a numberOfRowsInSection: method that resembles this:
- (NSInteger)numberOfRowsInSection:(NSInteger)section
{
// You should have a way to get section data with an index (section)
// The below example assumes an NSArray named self.mySectionCollection
// exists for this purpose
NSArray *sectionList = self.mySectionCollection;
// You should have data associated with a section. Below its assumed
// this data is wrapped in an NSDictionary. Data that might be in here
// could include a height, a name, number of rows, etc...
NSDictionary *sectionData = [sectionList objectAtIndex:section];
NSInteger rowCount = 0;
// SectionData should NEVER be nil at this point. You could raise an
// exception here, have a debug assert, or just return 0.
if (sectionData)
{
rowCount = [[sectionData objectForKey:#"rowCount"] intValue];
}
return rowCount;
}
You can store information about sections / rows in several different ways including using CoreData. The point is that by modifying this collection in between beginUpdates and endUpdates you are telling the UITableView how to update itself (it will query numberOfRowsInSection:, numberOfSections, and cellForRowAtIndexPath: as needed).
Edit
I believe if you modify your insertRows: method to modify your data source (messenges) at the same time you notify UITableView that updates have occurred that things will begin working for you properly.
Try using this code:
-(IBAction)insertRows:(id)sender
{
MDAppDelegate *appDelegate = (MDAppDelegate *)[[UIApplication sharedApplication] delegate];
UITableView *tV = (UITableView *)self.tableView;
[tV beginUpdates];
// UPDATE DATA MODEL IN BETWEEN beginUpdates and endUpdates
int rowIndex = appDelegate.messenges.count + 1;
[messenges insertObject:[[NSString alloc] initWithFormat:#"%#", _textField.text]
atIndex:rowIndex];
// Notify UITableView that updates have occurred
NSArray *insertIndexPaths = [NSArray arrayWithObject:
[NSIndexPath indexPathForRow:rowIndex inSection:1]];
[tV insertRowsAtIndexPaths:insertIndexPaths
withRowAnimation:UITableViewRowAnimationRight];
[tV endUpdates];
}
Edit #2
If you are still having issues, I would look at where you are setting the self.editing flag.
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section
{
MDAppDelegate *appDelegate = (MDAppDelegate *)[[UIApplication sharedApplication] delegate];
//add new variable to what rows will be +1
NSUInteger numberOfRowsInSection = appDelegate.messenges.count;
if (self.editing)
{
numberOfRowsInSection++;
}
return numberOfRowsInSection;
}
This flag controls whether an additional row exists in the table or not. For this reason, you must set it between beginUpdates and endUpdates like:
// assuming the editing flag is set from some IBAction
-addNewRow:(id)sender
{
int row = ???; // not sure where you want this new row
[self.tableView beginUpdates]
self.editing = YES;
NSArray *insertIndexPaths = [NSArray arrayWithObject:
[NSIndexPath indexPathForRow:row inSection:1]];
[self.tableView insertRowsAtIndexPaths:insertIndexPaths
withRowAnimation:UITableViewRowAnimationRight];
[self.tableView endUpdates];
}
Remember to similarly call deleteRowsAtIndexPaths:withRowAnimation: if the user stops editing if you are removing the row you add. I believe no action is necessary if the edit becomes permanent, but you'd need to set self.editing = NO while also adding a new row to it's proper place in self.messenges.
Also, in your insertRows: method, you telling the UITableView that you are inserting a row at index 1 when in fact you always insert at the end of the messenges collection. I've modified my version of the insertRows method so that it has a rowIndex variable for the purpose of ensuring the same index is used when updating the data model and informing UITableView of the change.
Lastly, please include as much debugging information as possible when you run into problems. Usually when a problem arises from updating the UITableView in the manner you are trying, it will tell you there is an inconsistency error. It's been awhile since I've seen it, but something to the effect that before updating the table there were 5 number of rows and after there were 7 when only 1 was added. I'd like to see this message if it is showing up in your console.
Edit #3
This is in response to the error you are seeing:
* Terminating app due to uncaught exception 'NSRangeException', reason: '* -[__NSArrayM objectAtIndex:]: index 4 beyond bounds [0 ..
3]'
Your error has nothing to do with inserting into the UITableView. It has to do with the fact that you are trying to insert beyond the bounds of an array. My guess is the offending line is:
[messenges insertObject:[[NSString alloc] initWithFormat:#"%#",
_textField.text]
atIndex:appDelegate.messenges.count+1];
To insert at the end of the array, use an index of appDelegate.messenges.count (removed the +1).
Also... Are you absolutely certain your data model and calls to update the UITableView agree? Try invoking NSLog(#"rowsBeforeUpdate: %i", [self numberOfRowsInSection:0]); just after you call beginUpdates and just before calling endUpdates. Also, call NSLog(#"inserting index paths: %#", insertIndexPaths) when informing the UITableView of an insert operation. My guess is that self.messenges accurately reflects the rows that should be in the table, but by adding +1 when self.tableView.editing is true, you push the numberOfRowsInSection: above what you report in your calls to insertRowsAtIndexPaths:withRowAnimation:.
Try this:
-(IBAction)insertRows:(id)sender {
MDAppDelegate *appDelegate = (MDAppDelegate *)[[UIApplication sharedApplication] delegate];
[self.tableView beginUpdates];
NSLog(#"rows before update: %i", [self numberOfRowsInSection:0]);
self.tableView.editing = YES;
NSLog(#"rows with editing flag set: %i", [self numberOfRowsInSection:0]);
int rowIndex = appDelegate.messenges.count; // don't need +1 at end
int sectionIndex = 0; // should it be 0 or 1? I would guess 0
[messenges insertObject:[[NSString alloc] initWithFormat:#"%#", _textField.text]
atIndex:rowIndex];
NSArray *insertIndexPaths = [NSArray arrayWithObject:
[NSIndexPath indexPathForRow:rowIndex
inSection:sectionIndex]];
[self.tableView insertRowsAtIndexPaths:insertIndexPaths
withRowAnimation:UITableViewRowAnimationRight];
NSLog(#"inserting index paths: %#", insertIndexPaths);
[self.tableView endUpdates];
}
I changed the row index above to exclude the +1 in it. I changed the section index to be 0, which should be correct unless this UITableView does in fact have multiple sections that haven't been mentioned. Make sure that your logs make sense. If you see numberOfRowsInSection: increase by two during the course of insertRows: then you had also better see insertRowsAtIndexPaths: reflect the same thing. I'm confused why you need the editing flag to add another row in your numberOfRowsInSection: method. This part (where you add an additional row if self.editing is set) doesn't seem to be working right. Perhaps just try leaving out this part to have some of it working, then add it back in once you have some things working properly. Perhaps change numberOfRowsInSection: to be like the following until some things begin working:
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section
{
MDAppDelegate *appDelegate = (MDAppDelegate *)[[UIApplication sharedApplication] delegate];
NSUInteger numberOfRowsInSection = appDelegate.messenges.count;
NSLog(#"numberOfRowsInSection %i: %u", section, numberOfRowsInSection);
return numberOfRowsInSection;
}

How do I prevent quickly tapping the Edit button for a UITableView multiple times from crashing the application?

I have a subclassed UITableViewController that performs some animations when editing mode is entered, namely removing the UIBarButtonSystemItemAdd from a navigation bar, and animating the first row of the table out of existence, and then back in when edit mode is exited:
- (void) setEditing: (BOOL) editing animated: (BOOL) animated
{
NSIndexPath *indexPath = [NSIndexPath indexPathForRow: 0 inSection: 0];
if (editing)
{
[self removeAddButton];
[datasource removeObjectAtIndex: 0];
[tableView deleteRowsAtIndexPaths: [NSArray arrayWithObject: indexPath] withRowAnimation: UITableViewRowAnimationTop];
}
else
{
[self restoreAddButton];
[datasource insertObject: #"First Row" atIndex: 0];
[tableView insertRowsAtIndexPaths: [NSArray arrayWithObject: indexPath] withRowAnimation: UITableViewRowAnimationTop];
}
[indexPath release];
[super setEditing: editing animated: animated];
}
Problem is, when the Edit button is tapped too quickly multiple times, and the animations haven’t finished yet, the application crashes with:
AppName(1824,0xa0ae5500) malloc: *** error for object 0x5f503a0: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
Obviously, something is going on where something being animated in or out isn’t having enough time to finish, so, how do I get around this? Am I doing something unnecessary/wrong?
Actually, this is a simple memory management error.
Remove this line:
[indexPath release];
The reason? This line:
NSIndexPath *indexPath = [NSIndexPath indexPathForRow: 0 inSection: 0];
This returns a NSIndexPath with an effective retain count of 0. You don't need to release it.
For any kind of memory release error, you should run again with NSZombieEnabled turned on. It will give you a more complete error message.
You should also get in the habit of using Build & Analyze; it probably would have flagged this.