All items of a UITableView disappear after deleting only one of them - iphone

Looks a trivial task, however I cannot find what I am doing wrong. I have a table view with sections and I am able to delete 1 item, but when I try to delete more than one item all items disappear from the table.
It is important to mention here that the scenario works well if I delete only 1 row. Deleting one row does not impact the items of the UITableView.
When I click the edit button, every row is candidate for deletion. Code is
[self.tableView setEditing:YES animated:YES];
When I am deleting a row the code calls:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
NSMutableArray *itemsOfGroup = [[table1 getItems] retain];
int section = indexPath.section;
section = section - 1;
MyItem *deleteItem = (MyItem*)[[[sections objectAtIndex:section] objectForKey:#"SectionEntries"] objectAtIndex:indexPath.row];
[itemsOfGroup removeObject:deleteItem];
[table1 setItems:[itemsOfGroup autorelease]];
[[[sections objectAtIndex:section] objectForKey:#"SectionEntries"] removeObjectAtIndex:indexPath.row];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject: indexPath] withRowAnimation:UITableViewRowAnimationRight];
[self createSections];
[self.tableView reloadData];
}
Is it possible to have a problem with my memory? (I think once the message "message sent to deallocated instance" appeared for an NSArray class)
Important to mention also that my items are not actually deleted from the data source. They only disappear from the table view.

Why are you autoreleasing itemsOfGroup when you've just set the Items in table 1 to it? Should be a release call after the set, or better to use the self... mechanism with properties. Check your memory allocations/deletions here. Are you throwing away all your content? Also, after your delete, what is your numberOfRowsInSection: and numberOfSections: returning? check those.

Related

Refreshing UITableview Section to One With Different Number of Rows

I've recently been working with a UITableView. It is dynamically populated once, then when a user selects an option, I want the list to change to a new one. I'm working with a grouped table with 3 sections and as you click on the rows the three groups need to be repopulated with a varying number of new rows. While my code works fine when there is the same number of rows in the new section as old, it crashes when that number changes. Interestingly though, it will wait to crash until it attempts to draw one of the cells that was there previously (the tableView still thinks the section has the old number of rows, tries to draw the cell that is no longer in my model object, and so I think it crashes because it's referencing a value in the new array that doesn't exist.
It crashes here:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = nil;
cell = [tableView dequeueReusableCellWithIdentifier:#"MyCell"];
if (cell == nil){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:#"MyCell"];
}
if (indexPath.section==2){
//CRASH BELOW
cell.textLabel.text = [NSString stringWithFormat:#"%#", (NSString *)[[[[storyArray objectAtIndex:pageNumber]getChoices] objectAtIndex:(unsigned long)indexPath.row]objectAtIndex:0]] ;
}
return cell;
}
The function I use to reload the table is here:
-(void)changePage:(int)pageChangeNumber{
NSLog(#"The page change! Changing to: %#",[[storyArray objectAtIndex:pageChangeNumber]getTitle]);
pageHeader.text=[[storyArray objectAtIndex:pageChangeNumber] getTitle];
pageBody.text=[[storyArray objectAtIndex:pageChangeNumber] getBody];
[myTableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationFade];
[myTableView reloadSections:[NSIndexSet indexSetWithIndex:1] withRowAnimation:UITableViewRowAnimationFade];
[myTableView reloadSections:[NSIndexSet indexSetWithIndex:2] withRowAnimation:UITableViewRowAnimationFade];
[myTableView reloadData];
pageNumber=pageChangeNumber;
NSLog(#"Page Change Done");
}
I've also changed the numberofRowsInSection to be dynamic...
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
NSLog(#"Recounting Rows");
if (section==2){
return [[[storyArray objectAtIndex:pageNumber]getChoices] count];
} else {
return 0;
}
}
Any ideas on how I can get this working when the number of rows per section changes?
Thanks!
I don't know what crash you're getting, but I ran into crashes if the numberOfSectionsInTableView: or tableView:numberOfRowsInSection: methods returned different numbers of rows while the table was restructuring itself.
For example, UITableView calls those methods many times while it is redrawing (including during animations). In my backend, some of the values were changing during animation.
I had to take special care to synchronize those values before changing the UITableView
Before you update your table view's data and call reloadSections you need to first call [myTableView beginUpdates] and once you're done [myTableView endUpdates]

How to give action to 'Delete' in editButtonItem?

I've in my tableview i've
self.navigationItem.rightBarButtonItem = self.editButtonItem;
clicking on the minus button shows Delete button. I want to know how to give an action to this Delete button.
Tableview shows a list of items from sqlite database.I'm using Xcode 4.2
The delete button is already linked to your table view, and tapping it will send tableView:commitEditingStyle:forRowAtIndexPath: to your table view's data source. You do your deletion here.
This method is normally included in the template for UITableViewController subclasses.
If you look in to the UITableViewDataSource Protocol Reference you will find in there tableView:commitEditingStyle:forRowAtIndexPath:.
Asks the data source to commit the insertion or deletion of a
specified row in the receiver.
In this method you will get the index path for the cell to delete. You have to remove the corespondent element in four data source. And you also need to remove the table view cell.
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
When you press delete button it will automatically call this delegate method of UITableView:
- (void)tableView:(UITableView *)tableView commitEditingStyle:( UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath.
{
// Check if it's a delete button or edit button...
if (editingStyle == UITableViewCellEditingStyleDelete)
{
// Update the array and table view.
[eventsArray removeObjectAtIndex:indexPath.row];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:YES];
// Commit the change.
NSError *error;
if (![managedObjectContext save:&error])
{
// Handle the error.
}
}
}
Implement tableView:commitEditingStyle:forRowAtIndexPath: in your table view data source (presumably your table view controller). See the docs on that method, or any of several Apple sample code projects (including what you get from the Xcode "master-detail app" template) for details & example usage.
I would add that you should make sure you are reloading the table data so it can update on the screen. Otherwise it will look like it didn't do anything.
-(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
[self.myMutableArray removeObjectAtIndex:indexPath.row];
[self.tableView reloadData];
}

Smooth reload of uitableview data when rows are deleted

I am trying to reload a uitableview when it scrolls to the bottom of the screen. I delete the first few rows and add more rows to the bottom. Before the news rows are added to the uitableview's data source, i call the [tableview reloaddata] method. This is because i want the tableview to display the row which were previously visible on it.
It reloads the data correctly but there is a sudden jerk in the tableview. It flashes which doesn't give a nice user experience. So my question is
How to update the uitableview
when few rows from the top are
deleted without having the
jerking/flashing effect?
The current visible row should be retained in the refreshed view also.
Any ideas? There are no crashes as i update the data source correctly.
TIA,
Praveen S
delete the data from the data source in tableView delegate.
-(void) tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *key = [keys objectAtIndex:indexPath.section];
NSMutableArray *nameSection =[names objectForKey:key];
int itemID=item.ID;
if ([nameSection count]==1) {
[self.keys removeObjectsInArray:[NSArray arrayWithObject:key]];
[tableView deleteSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:UITableViewRowAnimationFade];
}
else {
[nameSection removeObjectAtIndex:indexPath.row];
[self.names setValue:nameSection forKey:key];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
}
}
check this too

UITableView "swipe to delete" function freezes

I can't figure out why my tableView isn't updating after I tap the delete button.
Once I click it, the table view "freezes". If I click another row, so that the tableview goes to another level of the hierarchy and click back, I can see that the item has been deleted and everything works fine.
Here is my code:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
//[tableView beginUpdates];
if (editingStyle == UITableViewCellEditingStyleDelete)
{
// Do whatever data deletion you need to do...
[tableView beginUpdates];
NSManagedObject *obj = (NSManagedObject *)[entityArray objectAtIndex:indexPath.row];
[managedObjectContext deleteObject:obj];
NSError *error;
[managedObjectContext save:&error];
// Delete the row from the data source
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObjects:indexPath, nil] withRowAnimation:YES];
[self viewWillAppear:YES];
}
//[tableView endUpdates];
}
Any input on this problem would be appreciated.
Thanks.
Found same issue and stumbled upon this thread. But the reason for the tableview freeze issue was different in our case.
For the sake of posterity:
The UITableViewCell which goes into Edit mode to display the "insert" or "delete" buttons should never have its userInteractionEnabled property set to "NO".
By correcting this, the same tableview freezing issue was fixed for us.
I can't see a call to [tableView endUpdates] matching the [tableView beginUpdates] that is at start of the if.
Could it be for this reason that your table freezes?

reloadRowsAtIndexPaths:withRowAnimation: crashes my app

I got a strange problem with my UITableView: I use reloadRowsAtIndexPaths:withRowAnimation: to reload some specific rows, but the app crashes with an seemingly unrelated exception: NSInternalInconsistencyException - Attempt to delete more rows than exist in section.
My code looks like follows:
[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:0 inSection:0]] withRowAnimation:UITableViewRowAnimationFade];
When I replace that reloadRowsAtIndexPaths:withRowAnimation: message with a simple reloadData, it works perfectly.
Any ideas?
The problem is that you probably changed the number of items of your UITableView's data source. For example, you have added or removed some elements from/to the array or dictionary used in your implementation of the UITableViewDataSource protocol.
In that case, when you call reloadData, your UITableView is completely reloaded including the number of sections and the number of rows.
But when you call reloadRowsAtIndexPaths:withRowAnimation: these parameters are not reloaded. That causes the next problem: when you are trying to reload some cell, the UITableView checks the size of the datasource and sees that it has been changed. That results in a crash. This method can be used only when you want to reload the content view of the cell (for example, label has changed or you want to change its size).
Now if you want to remove/add cells from/to a UITableView you should use next approach:
Inform the UITableView that its size will be changed by calling method beginUpdates.
Inform about inserting new row(s) using method - (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation.
Inform about removing row(s) using method - (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation.
Inform the UITableView that its size has been changed by calling the method endUpdates.
I think the following code might work:
[self.tableView beginUpdates];
[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:0 inSection:0]] withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
I had this problem which was being caused by a block calling reloadRowsAtIndexPaths:withRowAnimation: and a parallel thread calling reloadData.
The crash was due to reloadRowsAtIndexPaths:withRowAnimation finding an empty table even though I'd sanity checked numberOfRowsInSection & numberOfSections.
I took the attitude that I don't really care if it causes an exception. A visual corruption I could live with as a user of the App than have the whole app crash out.
Here's my solution to this which I'm happy to share and would welcome constructive criticism. If there's a better solution I'm keen to hear it?
- (void) safeCellUpdate: (NSUInteger) section withRow : (NSUInteger) row {
// It's important to invoke reloadRowsAtIndexPaths implementation on main thread, as it wont work on non-UI thread
dispatch_async(dispatch_get_main_queue(), ^{
NSUInteger lastSection = [self.tableView numberOfSections];
if (lastSection == 0) {
return;
}
lastSection -= 1;
if (section > lastSection) {
return;
}
NSUInteger lastRowNumber = [self.tableView numberOfRowsInSection:section];
if (lastRowNumber == 0) {
return;
}
lastRowNumber -= 1;
if (row > lastRowNumber) {
return;
}
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section];
#try {
if ([[self.tableView indexPathsForVisibleRows] indexOfObject:indexPath] == NSNotFound) {
// Cells not visible can be ignored
return;
}
[self.tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationNone];
}
#catch ( NSException *e ) {
// Don't really care if it doesn't work.
// It's just to refresh the view and if an exception occurs it's most likely that that is what's happening in parallel.
// Nothing needs done
return;
}
});
}
After many try, I found "reloadRowsAtIndexPaths" can be only used in certain places if only change the cell content not insert or delete cells. Not any place can use it, even you wrap it in
[self beginUpdates];
//reloadRowsAtIndexPaths
[self endUpdates];
The places I found that can use it are:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
- (IBAction) unwindToMealList: (UIStoryboardSegue *) sender
Any try from other places like call it from "viewDidLoad" or "viewDidAppear", either will not take effect (For the cell already loaded I mean, reload will not take effect) or cause exception.
So try to use "reloadRowsAtIndexPaths" only in those places.
You should check cell visibility before reload. Here is Swift 3 code:
let indexPath = IndexPath(row: offset, section: 0)
let isVisible = tableView.indexPathsForVisibleRows?.contains{$0 == indexPath}
if let v = isVisible, v == true {
tableView.reloadRows(at: [indexPath], with: .automatic)
}
I had the same issue. In my case; it was happening only if another view controller pop/pushed over existing table view controller and then[self.tableView reloadRowsAtIndexPaths] function is called.
reloadRowsAtIndexPaths call was hiding/showing different rows in a table view which is having over 30, visually complex, rows. As i try to fix the issue i found that if i slightly scroll the table view app wasn't crashing. Also it wasn't crashing if i don't hide a cell (by returning 0 as height)
To resolve the issue, i simply changed the "(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath" function and returned at least 0.01 as row height.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
....
return rowModel.height + 0.01; // Add 0.01 to work around the crash issue.
}
it solved the issue for me.
THIS IS OLD. DO NOT USE.
I just bumped into this issue when I was calling reloadRowsAtIndexPaths... in order to change the cell to an editing cell containing a UITextField. The error told me I was deleting all of the rows in the table. To solve the problem, I removed:
[self.tableView beginUpdates];
NSArray *reloadIndexPath = [NSArray arrayWithObject:[NSIndexPath indexPathForRow:count inSection:section]];
[self.tableView reloadRowsAtIndexPaths:reloadIndexPath withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
and replaced it with
[self.tableView reloadData];
The app crashes because you have made some changes to your tableView. Either you have added or deleted some rows to the tableView. Hence when the view controller asks your model controller class for data, there is a mismatch in the indexPaths. Since the indexPaths have changed after modification.
So either you simply remove the call
[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:0 inSection:0]] withRowAnimation:UITableViewRowAnimationFade];
or replace it with
[self.tableView reloadData];
Calling reloadData checks your number of sections, number of rows in each section and then reloads the whole thing.
If data count changes completely, then use reloadData else, there is three functions to do it.
When data count changes we use insertRows / deleteRows and when data count still the same use reloadRows.
Important! don't forget call beginUpdates and endUpdates between insertRows/deleteRows/reloadRows calls.