I've got a UITableView which has been created programatically. I'm now trying to add the ability to delete the rows using the edit mode.
I've added the default way of doing this:
self.navigationItem.leftBarButtonItem = self.editButtonItem;
When you tap the button, it enters edit mode and the button changes to the 'done' button.
However, when I "slide to delete" the row, which just shows the delete button, the existing edit button doesn't change to 'done'.
Is there something I need to do extra because I created the table view programatically?
Below is the code for my tableview:
- (void)viewDidLoad
{
self.voucherTableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 36, 320, self.view.frame.size.height - 36) style:UITableViewStylePlain];
self.voucherTableView.autoresizingMask = UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth;
self.voucherTableView.delegate = self;
self.voucherTableView.dataSource = self;
[self.view addSubview:self.voucherTableView];
self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
// Default table view data source methods go here
- (void)setEditing:(BOOL)editing animated:(BOOL)animated
{
[super setEditing:editing animated:animated];
[self.voucherTableView setEditing:editing animated:animated];
}
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
return UITableViewCellEditingStyleDelete;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete)
{
// Commit the delete
}
}
Here is my answer for you: the button doesn't change to "Done" because a slide to delete does not actually have to be done in "Edit" mode and it does not put you into edit mode.
You could force it into Edit mode if you wish when you perform the delete, not sure why you would want to do that?
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete)
{
[tableView setEditing:YES animated:YES];
// Commit the delete
}
}
New functionality, current as of iOS 8
As long as you use self.editingItem it should now update correctly on its own. In fact, Apple's documentation for -tableView:commitEditingStyle:forRowAtIndexPath: now specifically says:
You should not call -setEditing:animated: within an implementation of this method. If for some reason you must, invoke it after a delay by using the -performSelector:withObject:afterDelay: method.
This is a change from the previous functionality that is mentioned in some of the other answers.
One caveat to note is that, as of iOS 8.1.2, if you fail to call -deleteRowsAtIndexPaths:withRowAnimation: in -tableView:commitEditingStyle:forRowAtIndexPath: (for instance by calling [self.tableView reloadData] after updating your data model instead) the editingItem will be stuck in the edit state.
#Scrooby: The edit button title and state won't change to "Done"
That is because "Edit" button click triggers editing mode of tableview, but the editing mode of the tableview does not trigger event to change button title to "Done".
Suppose Edit Button Click (Event A) & TableView in Editing Mode (Event B)
Event A implies Event B, but Event B does not imply Event A.
Hope you get it now. This is a simple logic.
Hope this helps you.
Try putting [super setEditing:editing animated:animated]; at the end of the method instead. It should work then.
Related
This might be a very stupid question, but I have very little knowledge about Xcode, Objective-C and iOS development in general.
What I have is a UITableViewController, with a number of cells. I've gotten the 'add' and delete (swipe to delete) functions to work, but I cannot figure out how to connect the 'edit' button to a function that will trigger the editing mode of that TableView, so I can rearrange the order of the elements.
I have un-commented these functions:
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the item to be re-orderable.
return YES;
}
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
XYZToDoItem *itemToMove = [self.toDoItems objectAtIndex:fromIndexPath.row];
[self.toDoItems removeObjectAtIndex:fromIndexPath.row];
[self.toDoItems insertObject:itemToMove atIndex:toIndexPath.row];
[self.tableView reloadData];
}
As you can see I have also made a few additions to the moveRowAtIndexPath method following the documentation.
In addition there are these functions;
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source
[self.toDoItems removeObjectAtIndex:indexPath.row];
[self.tableView reloadData];
}
}
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the specified item to be editable.
return YES;
}
As previously stated the "swipe to delete" function works, I don't know how, but I simply just uncommented these last two methods and edited one a bit to fit my code.
To be more specific, what I'm asking is how can I link the 'edit' button I have in my Navigation Controller to trigger the edit mode for the TableView, thereby showing the three lines (?) known from the TableViews editing mode (and letting me drag the elements to an order I want).
Thanks in advance for all your help! :)
If this is a UITableViewController you have two options. Either use the built-in edit button, which will give you the automatic Edit/Done button change. Use self.editButtonItem in your navigation bar:
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
Or you could use setEditing:animated to change editing state of the tableViewController:
// self is a UITableViewController
[self setEditing:YES animated:YES];
If your viewController is not a subclass of UITableViewController you can set the editing mode of the tableView:
[self.tableView setEditing:YES animated:YES];
If you have a UITableViewController you should use the first way because it is most convenient.
From the UITableViewCell showsReorderControl docs:
For the reordering control to appear, you must not only set this property but implement the UITableViewDataSource method tableView:moveRowAtIndexPath:toIndexPath:. In addition, if the data source implements tableView:canMoveRowAtIndexPath: to return NO, the reordering control does not appear in that designated row.
I've got the both in my tableviewController :
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
return YES;
}
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath {
NSLog(#"move from:%d to:%d", fromIndexPath.row, toIndexPath.row);
//just for test
}
My cell properties including re-order controls :
And yet I can't see the re-order control, what am I missing?
Have you put your UITableView into editing mode via
[tableView setEditing:YES animated:YES];
?
The first method you posted is needless, by the way, as that is the default behavior.
I think you need to actually get into edit mode for the table. Either try it in code when your view appears or create a button that will do it.
[tableView setEditing:YES animated:YES];
I have a UITableView with some custom cells in it. In these custom cells I defined a UILongPressGestureRecognizer that triggers the edit mode of this table. So when someone presses and holds a cell for like 1.5 sec, the table goes into edit mode.
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(startEditMode:)];
Which triggers:
- (void)startEditMode:(UISwipeGestureRecognizer *)recognizer {
if (self.allowEdit) {
UITableView *table = (UITableView *)self.superview;
[table setEditing:YES animated:YES];
}
}
But what I want to do is detect when the table goes into edit mode because I need to show/hide some additional buttons in this case. But for some reason in my viewcontroller this is never executed:
- (void)setEditing:(BOOL)editing animated:(BOOL)animated {
NSLog(#"SET EDITING");
[super setEditing:editing animated:animated];
}
Any suggestion why? Is this just being called when using a proper Edit Button as provided by default in the UINavigationController?
Or how can I detect when my UITableView goes into Edit Mode?
You're sending the message (setEditing) to the table view, you should be sending it to the view controller (presumably a UITableViewController subclass?). It will then take care of the table view for you.
Ok so in case someone else walks into this thread with the same problem, I will show you how I solved this.
In my custom UITableViewCell I have this method now:
- (void)startEditMode:(UISwipeGestureRecognizer *)recognizer {
if (self.allowEdit) {
UITableView *table = (UITableView *)self.superview;
UITableViewController *control = (UITableViewController *)table.dataSource;
[control setEditing:YES animated:YES];
}
}
Currently I do some work inside the didSelectRowAtIndexPath delegate and noticed that my row is still "selected" after I come back to the view controller later.
How can I unselect this row inside the delegate itself?
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
//do logic and ... it remains selected in the view?
}
[tableView deselectRowAtIndexPath:indexPath animated:YES];
Typically, you want to deselect the row when the view controller appears again:
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];
}
That's what UITableViewController does implicitly, and animating the deselection process after the view controller appears again is a helpful hint to the user as to which cell they selected previously.
This is odd. I'm right swiping a UITableViewCell in the iPad simulator. Even though the event below fires and the swipedCell is not nil, the Delete button doesn't appear. Actually, it appears-but only sometimes. I never get a bad access or a sigbart.
Here's the code:
- (void)handleSwipeFrom:(UISwipeGestureRecognizer *)recognizer
{
if (userListSwipeRightRecognizer.state == UIGestureRecognizerStateEnded) {
CGPoint swipeLocation = [userListSwipeRightRecognizer locationInView:self.outletView];
NSIndexPath *swipedIndexPath = [self.outletView indexPathForRowAtPoint:swipeLocation];
UITableViewCell* swipedCell = [self.outletView cellForRowAtIndexPath:swipedIndexPath];
[swipedCell setEditing:YES];
}
}
Is this just a simulator issue or am I doing something wrong?
If you simply want to enable swipe-to-delete on your table, there is a much easier way to do it. Implement tableView:commitEditingStyle:forRowAtIndexPath: in your data source and the table view will automatically show the delete button when a cell is swiped.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
If you defined your UITableView in your header, then try:
swipedCell = [self.outletView cellForRowAtIndexPath:swipedIndexPath];
If you use a custom cell and override setEditing, you must call the super method or your delete controls will not be drawn.
- (void)setEditing:(BOOL)editing animated:(BOOL)animated {
[super setEditing:editing animated:animated];
}