insertRowsAtIndexPaths Not Working, Crashing With Object At Index Out Of Bounds? - ios

Searched and searched.. can't find an answer :|
I'm pretty sure I'm using insertRowsAtIndexPaths right... Here's my code, along with the error I'm receiving...
First, I receive a response back from the server with comments that I want to insert dynamically into an empty section within my static uitableviewcontroller's tableview.
This is what happens when we receive the response from the server...
I count the number of comments returned, and generate an array of index paths for the empty section, which is section 1.
I then run the standard way of inserting rows with encapsulation between begin/end updates.
Great..
Now, here's my tableview's related code.
Everything runs fine once I get the response from the server, insert I assume works...
However, when I start scrolling down the view, as soon as I scroll right before where the first comment would show, I get a crash with the following error, and nothing pointing me in any direction as to what it could be
It's the strangest thing... and from my NSLog right before the crash I can see it's attempting to get cell for index path of section 1 row 0, which would be where the first comment SHOULD be... What's going on??
UPDATE
Here is the value of self.recentComments prior to the indexPaths array being created.
UPDATE 2
Here's the value of self.recentComments in the cellForRowAtIndexPath RIGHT before the crash.

This is actually very strange. What i would suggest is you just do [self.tableView reloadData] instead to insertRowsAtIndexPath.
You do this once you get the response from the server :
-(void)loadRecentCommentsResponse:(NSDictionary *)data
{
int status = [[[data objectForKey:#"meta"] onjectForKey:#"status"] intValue];
if(status == STATUS_OK){
self.recentComments = [[data objectForKey:#"response"] objectForKeys:#"comments"];
[self.tableView reloadData];
}
}
SInce you are not reloading the UITableView it is unable to recognize the number of row to be displayed in the numberOfRowsInSection method.
Update :
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *cellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
//Do your stuff here
return cell;
}
You missed the if(!cell) block. It is used so that if the cell is not initialized, it is initialized. Whenever you scroll the UITableView this `cellForRowAtIndexPath method is called everytime.
Thanks.

Related

Terminating with uncaught exception of type NSException when deleting row from UITableView

I'm working on an app that will update and add user's coordinate to a UITableView when the user has traveled over a preset distance interval. The coordinates will be automatically added to an NSMutableArray, and I use the array to update the table.
Everything load up and work fine (I can edit the table by moving and re-ordering the rows) but whenever I chose to delete a specific row, the program crash with the error "libc++abi.dylib: terminating with uncaught exception of type NSException".
- (void) tableView:( UITableView *) tableView commitEditingStyle:( UITableViewCellEditingStyle) editingStyle forRowAtIndexPath:( NSIndexPath *) indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
NSArray *items = [[G5SharedStore sharedStore]allCoords];
G5SharedStore *item = items[indexPath.row];
[[G5SharedStore sharedStore]removeItem:item];
//THIS IS WHERE THE ERROR OCCURS
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
}
The last line is what causes the error, but I'm new to objective-c, so I'm not sure how to fix this. Any help would be appreciated. Let me know if you need more details.
Thanks in advance.
**Edit:
Ok, I played around with the code this morning and it works. The problem is that the array keeps adding more and more items, but the tableview doesn't, unless I go back out and click "show table" button again to refresh it. So whenever I delete something in the tableview, the table's size is inconsistent with the array's size therefore I get the error. Here's my new problem, I tried to solve the above problem by making the table automatically update its data using:
[tableView reloadData];
The table does update, but it keeps adding blank cells ... with no data in it. Here's where I added the above:
- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#" UITableViewCell"
forIndexPath:indexPath];
NSArray *items = [[G5SharedStore sharedStore] allCoords];
G5SharedStore *item = items[indexPath.row];
cell.textLabel.text = [item description];
[tableView reloadData];
return cell;
}
Since I just started iOS programming 1 month ago, I could be wrong. So please guide me.
Thanks again everyone
I can see two problems:
Firstly this article recommends modifying the table view before the data-model (you are doing it the other way round):
It must do two things:
Send deleteRowsAtIndexPaths:withRowAnimation: or insertRowsAtIndexPaths:withRowAnimation: to the table view to direct
it to adjust its presentation.
Update the corresponding data-model array by either deleting the referenced item from the array or adding an item to the array.
Secondly you don't appear to be calling beginUpdates and endUpdates around that call. To quote the reference:
Note the behavior of this method when it is called in an animation
block defined by the beginUpdates and endUpdates methods.
Instead of delete row delete object from the array and reload table
- (void) tableView:( UITableView *) tableView commitEditingStyle:( UITableViewCellEditingStyle) editingStyle forRowAtIndexPath:( NSIndexPath *) indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
NSArray *items = [[G5SharedStore sharedStore]allCoords];
G5SharedStore *item = items[indexPath.row];
[[G5SharedStore sharedStore]removeItem:item];
//Remove this
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
//Add This Line
[tableView reloadData];
}
}

UITableView scrolling madness

I have a table view which gets data from a core data array. When I try to assign the value of the object "Post" to something or NSLog it the table scrolling lags.
Here is my code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"CellCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
Post *post = [array objectAtIndex:indexPath.row];
// If I comment out the NSLog the scroll is smoothly
NSLog(#"%#", post.title);
// Same thing for the line below
cell.textLabel.text = post.title;
return cell;
}
EDIT:
I'm using StackMob v1.2.0
Using NSLog will surely cause performance issues, especially in methods like cellForRowAtIndexPath which is invoked frequently.
Please check these article for details :
Dropping NSLog in release builds
The Evolution of a Replacement for NSLog
EDIT :
Also your implementation causes the slowness.
You are missing the allocation of tableviewCells.
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
First of all you should add the code that "Midhun MP" proposed.
If this doesnt help with the lagging problem...then i suppose post.title must be too heavy to be in that method (because that method gets called too many times)(Please let it not be that post.title uses internet connection to get the string?! Is it?)
Solution:
1)First thing in your app : Create an array2 and put in there all the post.title that u need(every time posts change you shoud update that array2)
2)use this array2 to get the text for the cells : cell.textLabel.textPost=[array2 objectAtIndex:indexPath.row];
3)I cant be 100% sure if NSLog is a problem because i havent tested that(could be..but this should only occur during debug...in release mode you shouldnt have it in there) but its easy to test and see(just comment the NSLog line)
Hope that helps.

Update section of static UITableView

I hope someone can help...
I've setup a static UITableView Controller with four sections. The top two sections are for inputting data. The third section has 1 cell and contains a save button.
The final section has 6 cells and I want to be able to populate these six cells with the data from the first two sections, in the long run anyway...
So i tried to a small example, but it's not working. The console returns the following error:
UITableView dataSource must return a cell from tableView:cellForRowAtIndexPath:'
* First throw call stack:
Here's an example:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
NSIndexPath *EditRow = [NSIndexPath indexPathForRow:1 inSection:4];
UITableViewCell *cell = [tableView cellForRowAtIndexPath:EditRow];
NSString *test = #"Test";
cell.textLabel.text = test;
NSLog(#"Index Path: %d", indexPath.row);
NSLog(#"Cell ID:, %#", CellIdentifier);
return cell;
}
So my question comes down to.. How do I update a specific cell in a specific section?
Thanks
When I implement static cells, I delete the cellForRowAtIndexPath, numberOfSectionsInTableView, and numberOfRowsInSection methods from the generic UIViewController stub that Xcode generates; these seem to be necessary only when you're dealing with dynamic cells.
Then, by control-dragging in the storyboard, I create named outlets for all UI elements in the cells that need to be set programmatically; in my case, these are usually UILabels. In the viewDidLoad method of the controller, I set the appropriate initial values of these outlets, for example:
self.nameLabel.text = self.dataSource.name;
Once you've got this wired up, you can change any of the values at will.
I hope that this works for you. I'd be happy to learn from anyone who has a better (more appropriate?) way of doing this.
Simply call - [UITableView (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation], then your data source will be called for the appropriate index paths.

Force UITableView to call cellForRowAtIndexPath: for all cells

I am having some trouble with UITableView's reloadData method. I have found that it only calls cellForRowAtIndexPath if there are new cells being added or taken away.
Example: I have five cells, each containing five strings. If I add a new cell and call reloadData, the table is updated and I see it. Yet if I go into one of the five cells, add a new string, then return and call reloadData, none of the table view's delegate methods is called.
My question: Is it possible to force the table view to completely reload the data in all of its visible cells?
I found the problem- I had my cell customization code in the if(cell == nil) block, so because the cells were being recycled, they weren't being changed. Taking my customization code out of that block fixed the problem.
I've found that reloading sections reloads data more readily. If you just have one section you can try:
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationNone];
You can try this
[tableView reloadData];
or
[tableView deleteRowsAtIndexPaths:[NSMutableArray arrayWithObjects:indexPath, nil] withRowAnimation:UITableViewRowAnimationFade];
put this in commitEditingStyle method.
Well, the table view's only going to call cellForRowAtIndexPath on the visible cells but if you do call reloadData it will do that so there's something else going on here and I'm not sure what it is.
Even if your tableview has 50 rows, there only will exist as much cells as can be visible at one time. That's the whole story behind the reuseIdentifier. So forcing 'all the cells' doesn't exist. If a new cell appears, the data is loaded dynamically.
The only way to change a cell is to change the data that is delivered by the dataSource method cellForRowAtIndexPath.
Do not take your code out of the if (cell == nil) block. Instead, create a representative identifier for the cell you're making; try and make sure that all of the cell's content is referred to in the identifier. For example, if you have 3 numbers showing, make sure to have those three numbers in the identifier in a unique way that would only refer to a cell that has such content.
Let's say you have three NSArray properties in your class, array1, array2, and array3 that have int values wrapped inside of NSNumber objects. You want to use those NSArrays to fill a UITableView, this is what I'd do:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *identifier = [NSString stringWithFormat:#"%#-%#-%#",
[[array1 objectAtIndex:indexPath.row] intValue],
[[array2 objectAtIndex:indexPath.row] intValue],
[[array3 objectAtIndex:indexPath.row] intValue]];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:identifier] autorelease];
//Build your cell here.
}
return cell;
}

EXEC_BAD_ACCESS in UITableView cellForRowAtIndexPath

My UITableView is returning EXEC_BAD_ACCESS, but why!
See this code snippet!
Loading the UITableView works fine, so allXYZArray != nil and is populated!
Then scrolling the tableview to the bottom and back up causes it to crash, as it goes to reload the method cellForRowAtIndexPath
It fails on line:
"NSLog(#"allXYZArray::count: %i", [allXYZArray count]);"
(UITableViewCell *)tableView:(UITableView *)theTableView cellForRowAt
IndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"CellIdentifier";
UITableViewCell *cell = [theTableView dequeueReusableCellWithIdentifier:CellIdentifier];
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
#try
{
if (allXYZArray == nil) {
NSLog(#"nil");
allXYZArray = [ToolBox getMergedSortedDictionaries:allXYZGiven SecondDictionary:allXYZSought];
}
NSLog(#"%i", [indexPath row]);
NSLog(#"allXYZArray::count: %i", [allXYZArray count]);
EXC_BAD_ACCESS means that your program is trying to access a memory address that is invalid or otherwise inaccessible from your process. This most commonly happens when you try send a message to an object that has already been dealloced. So the first step in debugging EXC_BAD_ACCESS is to figure out which object your program was trying to send a message to when the crash happened. Often the answer isn't obvious, in which case, NSZombieEnabled is a great tool for identifying which line of code caused the crash.
In your case you've already determined that the crash happens when you call [allXYZArray count], making allXYZArray our prime suspect. This object is being returned from +[ToolBox getMergedSortedDictionaries:SecondDictionary:], so it's likely that your bug is in the implementation of that method. I would guess that it's returning an object that has already been released instead of autoreleased, as prescribed by the Memory Management Programming Guide for Cocoa. (This is one of the most important documents in the SDK, by the way. I recommend rereading it once a month until its policies and techniques become second nature.)
Ok, reusing a cell, does not guarantee that the cell will be initialized properly:
UITableViewCell *cell = [theTableView dequeueReusableCellWithIdentifier:CellIdentifier];
cell will sometimes be null (especially the first time I guess).
Check cell for null and if so, initialize it properly.
if (cell == nil)
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];

Resources