UITableViewCell,滑动显示删除按钮


568

滑动时如何显示删除按钮UITableViewCell?该事件永远不会引发,并且删除按钮也不会出现。


1
任何需要详细且更新的答案的人,请滚动至stackoverflow.com/a/37719543/6872794
Munib,2016年

请参阅我的Swift 4答案以获取类似的问题,该问题最多显示3种不同的方式来创建滑动以删除UITableViewCells的动作。
Imanou Petit

我8年前问这个问题...请删除这个问题,它已经过时了。斯威夫特甚至不存在!
TheLearner '18

我们可以为侧面滑动按钮设置高度吗?例如:我的单元格是150,我希望按钮仅显示50.0f,这可能吗?
Suhas Patil

这在行上效果很好,但是有关如何集成各节的任何线索?
弗罗斯特·莫恩

Answers:


1035

在启动期间(-viewDidLoad or in storyboard)执行:

self.tableView.allowsMultipleSelectionDuringEditing = NO;

重写以支持表视图的条件编辑。仅在要退回NO某些物品时才需要执行此操作。默认情况下,所有项目都是可编辑的。

- (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
    }    
}

94
这可行,但是...-(BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath ...仅在您要为某些项目返回NO时才需要实现。默认情况下,所有项目都是可编辑的,因此如果您始终返回YES,则无需实施它。
Thanos Diacakis 2012年

24
同样重要的是要知道:这些是UITableViewDataSource方法,而不是 UITableViewDelegate方法。
戴夫·艾伯特


12
只是要清楚-您必须重写tableView:commitEditingStyle:forRowAtIndexPath:,否则将无法识别滑动手势,并且在尝试删除时不会发生任何事情。
克里斯,

起初,这对我不起作用。我还需要设置self.tableView.allowsMultipleSelectionDuringEditing = NO;向左滑动才能工作。在我看来,这听起来像是个错误,因为该表未处于编辑状态。此选项应仅应用“ DuringEditing”。但是,它现在可以工作,并且只要表格进入编辑状态,我都将其设置为YES。
osxdirk

118

该答案已更新至Swift 3

我总是认为有一个非常简单,独立的示例很好,这样在我学习新任务时不会做任何假设。答案是删除UITableView行。该项目的执行如下:

在此处输入图片说明

该项目基于SwiftUITableView示例

添加代码

创建一个新项目,然后将ViewController.swift代码替换为以下代码。

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.
        }
    }

}

上面代码中允许行删除的单键方法是最后一个。这里再次强调:

// 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.
    }
}

故事板

UITableView在情节提要中的View Controller中添加一个。使用自动布局将表格视图的四个边固定到视图控制器的边缘。控制从情节提要中的表格视图拖动到@IBOutlet var tableView: UITableView!代码中行。

已完成

就这样。您现在应该可以运行您的应用并通过向左滑动并点击“删除”来删除行。


变化

更改“删除”按钮的文本

在此处输入图片说明

添加以下方法:

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

自定义按钮动作

在此处输入图片说明

添加以下方法。

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]
}

请注意,这仅可从iOS 8使用。有关更多详细信息,请参见此答案

已针对iOS 11更新

可以使用添加到iOS 11中UITableViewDelegate API的方法在单元格的前面或后面放置动作。

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])
 }

进一步阅读


感谢您的示例和代码。我现在准备实现删除功能。您能否告诉我添加到viewDidLoad()中的“ self.tableView.registerClass(...”)行的用途是什么?与接口生成器中的等效是什么?在自定义单元格示例中没有。好像我们现在指定了cellReuseIdentifier两次,谢谢!
rockhammer

如果包含.registerClass行,则编译会失败
Rockhammer

@rockhammer,您是对的,您不必(显然不能)在代码和Interface Builder中设置单元重用标识符。只需根据自己的喜好选择一种方法。虽然这个项目是基于这个基本UITableView一个,这是一个完全独立的项目,你不需要做此处不再赘述什么。我开始在代码中设置它的原因是它在我的答案中需要较少的解释。我也应该回过头来编辑基本示例以使用代码。
Suragch

如何实现右滑动?是说向左滑动“拒绝”某项,向右滑动“接受”该单元格中的某项?
穆尼卜

1
据我所知,@ return0没有内置向右滑动功能,因此您必须从头开始创建它。如果您想尝试的话,请参阅本文以获取使您入门的想法。但是,我不建议您这样做,因为这不是用户期望的标准操作。而是,我会在上面的答案的“自定义按钮操作”部分中向左滑动显示两个按钮选择。
Suragch '16

70

此代码显示了如何实现删除。

#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];
    }
}

(可选)在您的初始化替代中,添加以下行以显示“编辑”按钮项:

self.navigationItem.leftBarButtonItem = self.editButtonItem;

您需要实现该方法。里面的内容应该与您的用例有意义的内容匹配。在上面的代码中,_chats是表视图的后备数据。一旦用户单击了delete,就应该从_chat中删除单个聊天对象,以便数据源可以反映新的行数(否则抛出异常)。
ewcy '17

25

我有一个刚刚解决的问题,所以我分享了它,因为它可能会对某人有所帮助。

我有一个UITableView并添加了显示的方法以使滑动能够删除:

- (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
    }    
}

我正在做一个更新,该更新使我可以将表置于编辑模式并启用多选。为此,我从Apple的TableMultiSelect示例中添加了代码。一旦工作正常,我就会发现删除功能已停止工作。

事实证明,将以下行添加到viewDidLoad是问题所在:

self.tableView.allowsMultipleSelectionDuringEditing = YES;

在此行中,多选将起作用,但要删除的滑动将不起作用。没有界限,那就是另一回事。

解决方法:

将以下方法添加到您的viewController中:

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

然后,在将表格置于编辑模式的方法中(例如,通过按下按钮),您应该使用:

[self setEditing:YES animated:YES];

代替:

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

这意味着仅当表处于编辑模式时才启用多选。


这很有帮助。我在情节提要中设置了allowMultipleSelection。这样就解决了。
Mark Suman 2014年

1
这解决了一个使我们发疯的问题。我现在知道,“轻扫删除”和“在编辑模式下批量删除”基本上是互斥的,并且在进入/离开编辑模式时必须控制它们。非常感谢您对此进行研究!
fbitterlich 2014年

18

在UITableViewDataSource下面将帮助您滑动删除

- (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 是一个NSMutableArray,然后重新加载tableView

迅速

 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()
    }
}

但这是UITableViewDataSource
HotJard 2015年

17

在iOS 8和Swift 2.0中,请尝试此操作,

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]
}

这是一个很好的答案,您也可以设置多个操作。
穆尼卜

11

@Kurbz的答案很棒,但是我想留下这个笔记,希望这个答案可以节省人们一些时间。

有时我的控制器中有这些行,并且它们使滑动功能不起作用。

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

如果使用UITableViewCellEditingStyleInsertUITableViewCellEditingStyleNone作为编辑样式,则滑动功能不起作用。您只能使用UITableViewCellEditingStyleDelete,这是默认样式。


1
就我而言,我希望能够滑动以删除,但随后还可以移动单元格。一个可移动单元格还会在该单元格的左侧获得此“删除”按钮,该按钮不适合我的设计,要删除此按钮,其编辑样式应为.none。我通过“ if tableView.isEditing {return .none} else {return .delete}”解决了这一问题

保存了我的axz花花公子。谢谢:)
Sourav Chandra

9

斯威夫特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]
}

1
好的,这将显示“删除”选项卡,但在您按下它时不会将其删除。您需要删除数据源中的对象并重新加载表吗?
user3069232 '17

是的“ //在indexPath的删除项目”基于indexPath的删除行的放置逻辑
Pratik Lad

8

另外,可以使用以下方法在SWIFT中实现

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

8

迅捷3

您要做的就是启用这两个功能:

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()
    }

}

7

我知道这是一个老问题,但是@Kurbz答案仅在Xcode 6.3.2和SDK 8.3上需要

我需要添加[tableView beginUpdates][tableView endUpdates](感谢@ bay.phillips 在这里

// 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];
}

6

当删除表格视图的单元格时,还必须删除索引为x的数组对象。

我认为您可以使用滑动手势将其删除。表格视图将调用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];
    }    
}

取出物体后。您必须重新加载tableview使用。在代码中添加以下行:

[tableView reloadData];

之后,您已成功删除该行。当您重新加载视图或向DataSource添加数据时,该对象将不再存在。

对于其他所有问题,来自Kurbz的答案都是正确的。

我只想提醒您,如果您想从DataSource数组中删除对象,则委托函数是不够的。

希望我能帮到您。



6
- (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是单元格内容来自的数组
Rahul K Rajan 2014年

2

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]
}

2

对于Swift,只需编写以下代码

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

对于Objective C,只需编写以下代码

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

2

对于swift4代码,首先启用编辑:

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

然后将删除操作添加到编辑委托中:

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]
}

0

迅捷4,5

要删除滑动单元格,有两种内置的UITableView方法。在TableView dataSource扩展中写入此方法。

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

//Declare this method in Viewcontroller 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
    }

0

如果要采用可扩散的数据源,则必须将委托回调移动到UITableViewDiffableDataSource子类。例如:

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)
            }
        }
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.