I've searched around, it seems that most recommendations are to simply [tableView reloadData].
However, I am after animation on deletion of the cell. If you can imagine a checklist, I add items to the checklist and once the item/task on the checklist (EG: "Get milk from milk bar") is completed I remove the item/task by tapping on it. At which point I want the cell to disappear with animation.
As mentioned above, most people just recommend a route which would involve me removing the item from the array it is held in and simply reloading the tableview, but that's completely void of animation.
Below is what I've tried as an alternative with little success.
[tableView beginUpdates]
[tableView deleteRowsAtIndexPaths:[self.taskArray objectAtIndex:indexPath.row] withRowAnimation:UITableViewRowAnimationFade];
[tableView endUpdates]
Note: The taskArray is populated with task's I enter. Additionally, the above code resides within the "didSelectRowAtIndexPath" method.
Any tips as to what im doing wrong or what I could do instead?
TLDR; I want to remove a cell with animation from the tableView simply by tapping on it.
Just a quick observation, the API is requesting that you provide an array of index paths and you only pass the actual item you're wanting for deletion.
What happens if you try:
[tableView beginUpdates];
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView endUpdates];
In your didSelectRowAtIndexPath
// Delete from the taskArray
[self.taskArray removeObjectAtIndex:indexPath.row];
// Remove row from the tableview
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
Related
I have the following code in tableView:didSelectRowAtIndexPath:
[tableView reloadData];
[tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
[tableView deselectRowAtIndexPath:indexPath animated:YES];
It deselects a table view cell tapped by the user (with animation) after the table view is reloaded. It has worked fine in iOS 7 and earlier, but in iOS 8 the animation isn't shown.
What changes were made in iOS 8 to cause this? And what's the best way to achieve the same effect in iOS 8?
What is the surrounding code that's causing you to call [tableView reloadData]?
Depending on how much processing you are doing in cellForRowAtIndexPath and the surrounding methods, this can be an expensive operation.
You can run:
[tableView beginUpdates];
// Update code here
[tableView endUpdates];
without having to reload the entire tableView datasource.
When didSelectRowAtIndexPath is called, the cell is already selected. So there shouldn't be any need to call the tableView's selectRowAtIndexPath method. Simply calling deselectRowAtIndexPath should do the trick after updating your cells
I am extending the learn iOS programming today tutorial to include delete functionality.
I have modified the tableView didSelectRowAtIndexPath: method thusly:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:NO];
ToDoItem *tappedItem = [self.toDoItems objectAtIndex:indexPath.row];
if (tappedItem.completed) {
[tableView beginUpdates];
[self.toDoItems removeObjectAtIndex:indexPath.row];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationBottom];
[tableView endUpdates];
} else {
tappedItem.completed = YES;
}
[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationNone];
}
In a section with three rows, it works as expected. Tapping produces a checkmark, tapping a check marked item deletes it. But if I tap the bottom row, it crashes with 'attempt to delete row 2 from section 0 which only contains 2 rows before the update'. Note this is happening when the other two rows are still there (my searches found numerous posts where there was a crash when someone was deleting the last remaining row--not the case here). The bottom row will mark itself completed just fine.
Also note, moving the array changing call out of the beginUpdates block changes the error from row 2 to row 3 ... contains 3 rows.
TIA for any assistance.
Edit:
I have fixed the problem by moving [tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationNone]; inside of the else block. Can someone explain why?
If you use deleteRowsAtIndexPaths, there is no point in trying to reload the row that you deleted. And, obviously, if you try to reload a cell for an indexPath that is no longer valid, then you will have the sort of problem you describe.
Let's say you have 10 rows, you don't want to say, effectively, "delete the tenth row; now reload the tenth row in a table that now only has nine rows." You can easily imagine why that is problematic.
In this case, you should remove the call to reloadRowsAtIndexPaths altogether. You only have to call reload... if the contents of some of the remaining cells change. If you're just inserting or deleting rows, then just do that, and no call to reloadRowsAtIndexPaths is needed.
I have an app in which I have a UITableview with custom cells and headers. The cells have an inputView so when selected they become first responder and allow the user to input data.
I want to be able to update the visible TableViewCell and header information on the fly while the user is changing it.. easy, just call [tableview reloadData]; ..
Unfortunately this causes the inputview to resign first responder and hide itself.
Is there any way that I can get a reference to the cell itself inside the UITableview so that I can just change the text property? (cellForRow:atIndexPath: returns a new object with the same properties so doesn't work) It seems like the only easy solution may be to store a reference the cells in a dictionary each time a cell is populated, not really the ideal solution.
cellForRowAtIndexPath is literally just
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
CustomCell *orderCell;
static NSString *productCellIdentifier = #"ImageDetailCellIdentifier";
orderCell = [self.tableView dequeueReusableCellWithIdentifier:productCellIdentifier forIndexPath:indexPath];
//set a bunch of properties orderCell.blah
return orderCell;
}
According to UITableView documentation, -cellForRowAtIndexPath: returns an object representing a cell of the table or nil if the cell is not visible or indexPath is out of range.
That is also how I remember it. I don't think your observation is correct that it returns a new object. If the cell is visible you will get hold of it.
[tableView beginUpdates];
[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade]; ///as your choice in animation
[tableView endUpdates];
or else
[tableView beginUpdates];
// do some work what ever u need
[tableView endUpdates];
For reloading specific rows, you can use
- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation;
For example,
NSIndexPath* indexPath = [NSIndexPath indexPathForRow:2 inSection:1];
NSArray* indexArray = [NSArray arrayWithObject:indexPath];
[self.tableView beginUpdates];
[self.tableView reloadRowsAtIndexPaths:indexArray withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
I have a table view, in which if you select a row, it add a picker below the row. But if another row is selected, it hides the first picker (removed the row with the picker) and then adds a new picker.
Now my question is, how to I wait for the remove animation to finish before adding a new row?
This is how I add a new row. It's in the didSelectRow method.
NSIndexPath *selectedIndex = [NSIndexPath indexPathForRow:indexPath.row + 1 inSection:indexPath.section];
pickerInsertedAtIndexPath = selectedIndex;
NSArray *indexes = #[selectedIndex];
[tableView beginUpdates];
[tableView insertRowsAtIndexPaths:indexes withRowAnimation:UITableViewRowAnimationMiddle];
[tableView endUpdates];
This is how I remove a row. This is in the willSelectRow method.
[tableView beginUpdates];
[tableView deleteRowsAtIndexPaths:indexes withRowAnimation:UITableViewRowAnimationMiddle];
[tableView endUpdates];
Unfortunately there are no callbacks as to when the table view animations end. You'll have to wait for an interval of your choice before adding the new picker.
Not sure if this matches your scenario but can you use:
tableView:didEndDisplayingCell:forRowAtIndexPath:
A UITableViewDelegate callback that will inform you when a cell has been removed from the UITableView?
I have spent hours searching for the solution with out any luck. I am trying to delete a row (also deselect same row) programmatically. After row deletion call below, UITableViewDelgate methods get called expectedly and data source is updated but UITableView is not refreshed. deselectRowAtIndexPath call also does not work. I tried all kinds of scenarios as shown by commented lines.
Here is my code:
checkoutPerson is called as a result of observer listening for NSNotificationCenter messages.
- (void) checkoutPerson: (NSNumber*) personId {
Person *person = [_people objectForKey:personId];
if( person )
{
// Remove person from data source
int rowIndex = person.rowIndex;
S2Log(#"Deleting row number=%d", rowIndex);
[_allKeys removeObjectAtIndex:rowIndex];
[_people removeObjectForKey: personId];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:rowIndex inSection:0];
//[[self tableView] beginUpdates];
[self.tableView deselectRowAtIndexPath:indexPath animated:YES];
S2Log(#"Deleting indexPath row=%d", [indexPath row]);
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
//[[self tableView] endUpdates];
S2Log(#"Reloading data");
//[[self tableView] reloadData];
//[self performSelector:#selector(refreshView) withObject:nil afterDelay:1.5];
//[self.tableView performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:YES];
}
}
I will appreciate for help.
Thanks
-Virendra
I believe deleted cell is not being recycled. If I delete row in the middle, last row is always erased (since there is one less item) but the deleted row remains.
Use the above code between two function for table view
[tableView beginUpdates];
// the deletion code from data source and UITableView
[tableView endUpdates];
By calling this functions you are telling UITableView that you are about to make updates for deleting your cell.
Edit
The other problem I see with your code is you first delete the data from the data source.
Now you are asking for the UITableViewCell (which actually reloads the UITableView)
and then you are deleting the row from UITableView
I guess you should fetch the UITableViewCell before deleting values from your data source.
I found the problem. It has nothing to do with the code I posted above. It is syncing problem between visual display and the contents of data source. I have an embedded UITableView as part of a composite view. In composite view's controller, I was wiring up UITableView's delegate and data source to an instance of UITableViewController. Instead of this, I should have set UITableViewController's tableView property to the embedded UITableView. It seems that UITableView has to be contained within UITableViewController in order to correctly sync up table view visual display to the contents of data source. This also fixes row deselection and scrolling. I also needed to delay reloadData call in which case deleteRowsAtIndexPaths:withRowAnimation is not required. All you need is to modify the contents of your data source and call reloadData with a delay of 1.5 Seconds.
Thanks to all for great help.