ChatGPT解决这个技术问题 Extra ChatGPT

UITableViewCell, show delete button on swipe

How do I get the delete button to show when swiping on a UITableViewCell? The event is never raised and the delete button never appears.

See my Swift 4 answer for a similar question that shows up to 3 different ways to create swipe to delete actions for UITableViewCells.
I asked this question 8 years ago...please delete this question it’s massively outdated. Swift didn’t even exist!
can we make the height fix for the side swipe buttons? eg: my cell is 150 and i want button to be show only 50.0f is it possible?
this works great on rows, but any clues on how to integrate it sections?

L
Lawrence Gimenez

During startup in (-viewDidLoad or in storyboard) do:

self.tableView.allowsMultipleSelectionDuringEditing = false

Override to support conditional editing of the table view. This only needs to be implemented if you are going to be returning NO for some items. By default, all items are editable.

- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
    // Return YES if you want the specified item to be editable.
    return YES;
}

// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
    if (editingStyle == UITableViewCellEditingStyleDelete) {
        //add code here for when you hit delete
    }    
}

This works, but... - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath ... only needs to be implemented if you are going to be returning NO for some items. By default, all items are editable, so you need not implement it if you are always returning YES.
Also important to know: these are UITableViewDataSource methods and NOT UITableViewDelegate methods.
Just to be clear - You MUST override tableView:commitEditingStyle:forRowAtIndexPath: or the swipe gesture will not be recognized, and nothing will happen when you try to delete.
This did not work for me (at first). I also needed to set self.tableView.allowsMultipleSelectionDuringEditing = NO; for the left-swipe to work. This sounds like a bug to me because the table is NOT in editing state. This option should only apply "DuringEditing". However, it works now and I set it to YES whenever the table is entering editing state.
S
Stephen

This answer has been updated to Swift 3

I always think it is nice to have a very simple, self-contained example so that nothing is assumed when I am learning a new task. This answer is that for deleting UITableView rows. The project performs like this:

https://i.stack.imgur.com/sCoUS.gif

This project is based on the UITableView example for Swift.

Add the Code

Create a new project and replace the ViewController.swift code with the following.

import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    // These strings will be the data for the table view cells
    var animals: [String] = ["Horse", "Cow", "Camel", "Pig", "Sheep", "Goat"]

    let cellReuseIdentifier = "cell"

    @IBOutlet var tableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()

        // It is possible to do the following three things in the Interface Builder
        // rather than in code if you prefer.
        self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellReuseIdentifier)
        tableView.delegate = self
        tableView.dataSource = self
    }

    // number of rows in table view
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.animals.count
    }

    // create a cell for each table view row
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        let cell:UITableViewCell = self.tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as UITableViewCell!

        cell.textLabel?.text = self.animals[indexPath.row]

        return cell
    }

    // method to run when table view cell is tapped
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        print("You tapped cell number \(indexPath.row).")
    }

    // this method handles row deletion
    func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {

        if editingStyle == .delete {

            // remove the item from the data model
            animals.remove(at: indexPath.row)

            // delete the table view row
            tableView.deleteRows(at: [indexPath], with: .fade)

        } else if editingStyle == .insert {
            // Not used in our example, but if you were adding a new row, this is where you would do it.
        }
    }

}

The single key method in the code above that enables row deletion is the last one. Here it is again for emphasis:

// this method handles row deletion
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {

    if editingStyle == .delete {

        // remove the item from the data model
        animals.remove(at: indexPath.row)

        // delete the table view row
        tableView.deleteRows(at: [indexPath], with: .fade)

    } else if editingStyle == .insert {
        // Not used in our example, but if you were adding a new row, this is where you would do it.
    }
}

Storyboard

Add a UITableView to the View Controller in the storyboard. Use auto layout to pin the four sides of the table view to the edges of the View Controller. Control drag from the table view in the storyboard to the @IBOutlet var tableView: UITableView! line in the code.

Finished

That's all. You should be able to run your app now and delete rows by swiping left and tapping "Delete".

Variations

Change the "Delete" button text

https://i.stack.imgur.com/OfzBt.png

Add the following method:

func tableView(_ tableView: UITableView, titleForDeleteConfirmationButtonForRowAt indexPath: IndexPath) -> String? {
    return "Erase"
}

Custom button actions

https://i.stack.imgur.com/G5RPZ.png

Add the following method.

func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {

    // action one
    let editAction = UITableViewRowAction(style: .default, title: "Edit", handler: { (action, indexPath) in
        print("Edit tapped")
    })
    editAction.backgroundColor = UIColor.blue

    // action two
    let deleteAction = UITableViewRowAction(style: .default, title: "Delete", handler: { (action, indexPath) in
        print("Delete tapped")
    })
    deleteAction.backgroundColor = UIColor.red

    return [editAction, deleteAction]
}

Note that this is only available from iOS 8. See this answer for more details.

Updated for iOS 11

Actions can be placed either leading or trailing the cell using methods added to the UITableViewDelegate API in iOS 11.

func tableView(_ tableView: UITableView,
                leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration?
 {
     let editAction = UIContextualAction(style: .normal, title:  "Edit", handler: { (ac:UIContextualAction, view:UIView, success:(Bool) -> Void) in
             success(true)
         })
editAction.backgroundColor = .blue

         return UISwipeActionsConfiguration(actions: [editAction])
 }

 func tableView(_ tableView: UITableView,
                trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration?
 {
     let deleteAction = UIContextualAction(style: .normal, title:  "Delete", handler: { (ac:UIContextualAction, view:UIView, success:(Bool) -> Void) in
         success(true)
     })
     deleteAction.backgroundColor = .red

     return UISwipeActionsConfiguration(actions: [deleteAction])
 }

Further reading

How To Make A Swipeable Table View Cell With Actions – Without Going Nuts With Scroll Views

Documentation


thanks for the examples & code. I'm now ready to implement the delete function. Can you please tell me what is the purpose of the "self.tableView.registerClass(..." line you added to viewDidLoad()? And what is the equivalent of that in interface builder? That was not in the custom cell example. Seem like we are specifying cellReuseIdentifier twice now. Thanks!
If including .registerClass line, compile fails
@rockhammer, You're right, you don't need to (apparently can't) set the cell reuse identifier in both code and the Interface Builder. Just choose one way according to your preference. Although this project is based on that basic UITableView one, this is a completely stand alone project and you don't need to do anything that isn't described here. The reason I started setting it in code is that it requires less explanation in my answers. I should go back and edit the basic example to use code, too.
How would one implement a right swipe? Say a left swipe "rejects" something and a right swipe "accepts" something in the cell?
@return0, as far as I know, right swipe functionality is not built in, so you would have to create it from scratch. See this article for ideas to get you started if you want to try. However, I wouldn't recommend doing that since it isn't a standard action that a user would expect. Rather, I would show two button choices on a left swipe as in the custom button action section in my answer above.
m
ma11hew28

This code shows how to implement the delete.

#pragma mark - UITableViewDataSource

// Swipe to delete.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (editingStyle == UITableViewCellEditingStyleDelete) {
        [_chats removeObjectAtIndex:indexPath.row];
        [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
    }
}

Optionally, in your initialization override, add the line below to show the Edit button item:

self.navigationItem.leftBarButtonItem = self.editButtonItem;

You need to implement that method. The content inside should match with whatever makes sense to your use case. In the code above _chats is the backing data for the table view. Once the user hits delete, the individual chat object should be removed from _chat so that the data source would then reflect the new row count (otherwise throwing exception).
L
Leon

I had a problem which I have just managed to solve so I am sharing it as it may help someone.

I have a UITableView and added the methods shown to enable swipe to delete:

- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
    // Return YES if you want the specified item to be editable.
    return YES;
}

// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
    if (editingStyle == UITableViewCellEditingStyleDelete) {
        //add code here for when you hit delete
    }    
}

I am working on an update that allows me to put the table into edit mode and enables multiselect. To do that I added the code from Apple's TableMultiSelect sample. Once I got that working I found that my swipe the delete function had stopped working.

It turns out that adding the following line to viewDidLoad was the issue:

self.tableView.allowsMultipleSelectionDuringEditing = YES;

With this line in, the multiselect would work but the swipe to delete wouldn't. Without the line it was the other way around.

The fix:

Add the following method to your viewController:

- (void)setEditing:(BOOL)editing animated:(BOOL)animated
{
    self.tableView.allowsMultipleSelectionDuringEditing = editing; 
    [super setEditing:editing animated:animated];
}

Then in your method that puts the table into editing mode (from a button press for example) you should use:

[self setEditing:YES animated:YES];

instead of:

[self.tableView setEditing:YES animated:YES];

This means that multiselect is only enabled when the table is in editing mode.


This was helpful. I had set allowsMultipleSelection in the Storyboard. This fixed it.
This has solved a problem that drove us nuts. I now understand that "swipe to delete" and "batch deletion in edit mode" are basically mutually exclusive and you have to control that when entering/leavin edit mode. Thanks a lot for researching this!
i
iAnkit

Below UITableViewDataSource will help you for swipe delete

- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
    // Return YES if you want the specified item to be editable.
    return YES;
}

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
    if (editingStyle == UITableViewCellEditingStyleDelete) {
        [arrYears removeObjectAtIndex:indexPath.row];
        [tableView reloadData];
    }
}

arrYears is a NSMutableArray and then reload the tableView

Swift

 func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
            return true
        }

func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
    if editingStyle == UITableViewCellEditingStyleDelete {
        arrYears.removeObjectAtIndex(indexPath.row)
        tableView.reloadData()
    }
}

But it is UITableViewDataSource
J
Jessedc

In iOS 8 and Swift 2.0 please try this,

override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
   // let the controller to know that able to edit tableView's row 
   return true
}

override func tableView(tableView: UITableView, commitEdittingStyle editingStyle UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath)  {
   // if you want to apply with iOS 8 or earlier version you must add this function too. (just left in blank code)
}

override func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [UITableViewRowAction]?  {
   // add the action button you want to show when swiping on tableView's cell , in this case add the delete button.
   let deleteAction = UITableViewRowAction(style: .Default, title: "Delete", handler: { (action , indexPath) -> Void in

   // Your delete code here.....
   .........
   .........
   })

   // You can set its properties like normal button
   deleteAction.backgroundColor = UIColor.redColor()

   return [deleteAction]
}

This is a good answer, with this you can setup multiple actions too.
B
Brian

@Kurbz's answer is awesome, but I want to leave this note and hope this answer can save people some time.

I occasionally had these lines in my controller, and they made the swiping feature not working.

- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath{
    return UITableViewCellEditingStyleNone; 
}

If you use UITableViewCellEditingStyleInsert or UITableViewCellEditingStyleNone as the editing style, then the swiping feature doesn't work. You can only use UITableViewCellEditingStyleDelete, which is the default style.


In my case I wanted to be able to swipe to delete, but then also be able to move my cells. A moveable cell also gets this "delete" button on the left side of the cell, which didn't fit in my design and to remove this the editing style should be .none. I solved this by "if tableView.isEditing { return .none } else { return .delete }"
Saved my axz dude. Thanks :)
A
Allen

Swift 4

func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
    let delete = UITableViewRowAction(style: .destructive, title: "delete") { (action, indexPath) in
        // delete item at indexPath
    tableView.deleteRows(at: [indexPath], with: .fade)

    }
    return [delete]
}

Ok this makes the delete tab appear, but doesn't delete it when you press it. You need to delete the object in the datasource and reload the table yes?
yes "// delete item at indexPath" place logic of your delete row based on indexPath
Has been deprecated in iOS 14.
D
DrPatience

Also, this can be achieved in SWIFT using the method as follows

func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
    if (editingStyle == UITableViewCellEditingStyle.Delete){
        testArray.removeAtIndex(indexPath.row)
        goalsTableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic)
    }
}

M
Machado

Swift 3

All you have to do is enable these two functions:

func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {

    return true

}

func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {

    if editingStyle == UITableViewCellEditingStyle.delete {
        tableView.reloadData()
    }

}

b
byJeevan

I know is old question, but @Kurbz answer just need this for Xcode 6.3.2 and SDK 8.3

I need add [tableView beginUpdates] and [tableView endUpdates] (thanks to @bay.phillips here)

// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle: (UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
    // Open "Transaction"
    [tableView beginUpdates];

    if (editingStyle == UITableViewCellEditingStyleDelete) {
        // your code goes here
        //add code here for when you hit delete
        [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
     }

    // Close "Transaction"
    [tableView endUpdates];
}

M
Martin de Keijzer

When you remove a cell of your tableview, you also have to remove your array object at index x.

I think you can remove it by using a swipe gesture. The table view will call the Delegate:

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
    if (editingStyle == UITableViewCellEditingStyleDelete) {
        //add code here for when you hit delete
        [dataSourceArray removeObjectAtIndex:indexPath.row];
    }    
}

After removing the object. You have to reload the tableview use. Add the following line in your code:

[tableView reloadData];

after that, you have deleted the row successfully. And when you reload the view or adding data to the DataSource the object will not be there anymore.

For all other is the answer from Kurbz correct.

I only wanted to remind you that the delegate function won't be enough if you want to remove the object from the DataSource array.

I hope I have helped you out.


P
P.J.Radadiya
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath 
{
    if (editingStyle == UITableViewCellEditingStyleDelete)
    {
        //add code here for when you hit delete
        [dataSourceArray removeObjectAtIndex:indexPath.row];
        [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
    }    
}    

dataSourceArray is the array from which the cell contents comes
A
Austin Conlon

If you're adopting diffable data sources, you'll have to move the delegate callbacks to a UITableViewDiffableDataSource subclass. For example:

class DataSource: UITableViewDiffableDataSource<SectionType, ItemType> {

    override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
        return true
    }

    override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
        if editingStyle == .delete {
            if let identifierToDelete = itemIdentifier(for: indexPath) {
                var snapshot = self.snapshot()
                snapshot.deleteItems([identifierToDelete])
                apply(snapshot)
            }
        }
    }
}

Could you please elaborate how to use this class in controller?
b
budiDino

Swift 4,5

To delete a cell on swipe there are two built in methods of UITableView.Write this method in TableView dataSource extension.

func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
  let delete = deleteProperty(at: indexPath)
  return UISwipeActionsConfiguration(actions: [delete])
}

// Declare this method in UIViewController Main and modify according to your need

func deleteProperty(at indexpath: IndexPath) -> UIContextualAction {
  let action = UIContextualAction(style: .destructive, title: "Delete") { (action, view, completon) in
    self.yourArray.remove(at: indexpath) // Removing from array at selected index

    completon(true)
    action.backgroundColor = .red //cell background color
  }
  return action
}

A
Abdoelrhman

for swift4 code, first enable editing:

func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
    return true
}

then you add delete action to the edit delegate:

func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
    let action = UITableViewRowAction(style: .destructive, title: "Delete") { (_, index) in
        // delete model object at the index
        self.models[index.row]
        // then delete the cell
        tableView.beginUpdates()
        tableView.deleteRows(at: [index], with: .automatic)
        tableView.endUpdates()

    }
    return [action]
}

l
lcl

Swift 2.2 :

override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
    return true
}

override func tableView(tableView: UITableView,
    editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [UITableViewRowAction]? {
    let delete = UITableViewRowAction(style: UITableViewRowActionStyle.Default, title: "DELETE"){(UITableViewRowAction,NSIndexPath) -> Void in

    print("Your action when user pressed delete")
}
let edit = UITableViewRowAction(style: UITableViewRowActionStyle.Normal, title: "EDIT"){(UITableViewRowAction,NSIndexPath) -> Void in

    print("Your action when user pressed edit")
}
    return [delete, block]
}

P
PT Vyas

For Swift, Just write this code

func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
        if editingStyle == .Delete {
            print("Delete Hit")
        }
}

For Objective C, Just write this code

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
       if (editingStyle == UITableViewCellEditingStyleDelete) {           
            NSLog(@"index: %@",indexPath.row);
           }
}

H
Haseeb Javed

SWIFT 5 : for iOS 13+

func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
        return true
  }
    
    
 func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
        let deleteAction = UIContextualAction(style: .destructive, title: "Delete") {  (contextualAction, view, boolValue) in
            //Code I want to do here
        }
        
        let editAction = UIContextualAction(style: .destructive, title: "Edit") {  (contextualAction, view, boolValue) in
            //Code I want to do here
        }
        
        let swipeActions = UISwipeActionsConfiguration(actions: [deleteAction, editAction])
        
        return swipeActions
    }