【问题标题】:UITableViewCell, show delete button on swipeUITableViewCell,在滑动时显示删除按钮
【发布时间】:2011-03-19 13:52:56
【问题描述】:

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

【问题讨论】:

  • 请参阅my Swift 4 answer for a similar question,其中显示了多达 3 种不同的方式来为 UITableViewCells 创建滑动删除操作。
  • 我 8 年前问过这个问题...请删除这个问题,它已经过时了。斯威夫特甚至不存在!
  • 我们可以修复侧滑按钮的高度吗?例如:我的单元格是 150,我希望按钮只显示 50.0f 可能吗?
  • 这在行上效果很好,但是关于如何整合它的任何线索?

标签: ios uitableview cocoa-touch uikit


【解决方案1】:

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

【讨论】:

  • 这行得通,但是... - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath ... 仅当您要返回 NO 时才需要实现一些项目。默认情况下,所有项目都是可编辑的,因此如果您总是返回 YES,则无需实现它。
  • 同样重要的是要知道:这些是 UITableViewDataSource 方法和 NOT UITableViewDelegate 方法。
  • 明确一点 - 您必须覆盖 tableView:commitEditingStyle:forRowAtIndexPath: 否则将无法识别滑动手势,并且在您尝试删除时不会发生任何事情。
  • 这对我不起作用(起初)。我还需要设置self.tableView.allowsMultipleSelectionDuringEditing = NO; 才能使左滑起作用。这对我来说听起来像是一个错误,因为该表未处于编辑状态。此选项应仅适用于“编辑期间”。但是,它现在可以工作了,只要表格进入编辑状态,我就将其设置为 YES。
【解决方案2】:

此答案已更新为 Swift 3

我一直认为有一个非常简单、自包含的示例是很好的,这样当我学习一项新任务时就不会假设任何事情。这个答案是删除UITableView 行。项目执行如下:

本项目基于UITableView example for Swift

添加代码

创建一个新项目并将 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 添加到情节提要中的视图控制器。使用自动布局将表格视图的四个边固定到视图控制器的边缘。控制从情节提要中的表格视图拖动到代码中的@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。有关详细信息,请参阅 this answer

已针对 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。谢谢!
  • 如果包含 .registerClass 行,编译失败
  • @rockhammer,你是对的,你不需要(显然不能)在代码和 Interface Builder 中设置单元重用标识符。只需根据您的喜好选择一种方式即可。尽管此项目基于that basic UITableView one,但这是一个完全独立的项目,您无需执行此处未描述的任何操作。我开始在代码中设置它的原因是我的答案需要较少的解释。我也应该回去编辑基本示例以使用代码。
  • 如何实现向右滑动?说在单元格中向左滑动“拒绝”某事,向右滑动“接受”某事?
  • @return0,据我所知,右滑功能不是内置的,所以你必须从头开始创建它。如果您想尝试,请参阅this article 以获取帮助您入门的想法。但是,我不建议这样做,因为这不是用户期望的标准操作。相反,我会在左侧滑动中显示两个按钮选择,就像我上面答案中的自定义按钮操作部分一样。
【解决方案3】:

这段代码展示了如何实现删除。

#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 是表视图的支持数据。用户点击删除后,应从 _chat 中删除单个聊天对象,以便数据源反映新的行数(否则抛出异常)。
【解决方案4】:

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

我有一个 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];

这意味着只有在表格处于编辑模式时才启用多选。

【讨论】:

  • 这很有帮助。我在情节提要中设置了allowsMultipleSelection。这解决了它。
  • 这解决了一个让我们抓狂的问题。我现在明白“滑动删除”和“编辑模式下的批量删除”基本上是互斥的,你必须在进入/离开编辑模式时控制它。非常感谢您对此进行研究!
【解决方案5】:

下面的 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
【解决方案6】:

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

【讨论】:

  • 这是一个很好的答案,您也可以设置多个操作。
【解决方案7】:

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

我的控制器中偶尔会出现这些线条,它们导致滑动功能无法正常工作。

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

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

【讨论】:

  • 就我而言,我希望能够滑动删除,但也能够移动我的单元格。一个可移动的单元格也会在单元格的左侧获得这个“删除”按钮,这不适合我的设计,要删除它,编辑样式应该是 .none。我通过“if tableView.isEditing { return .none } else { return .delete }”解决了这个问题
  • 救了我的 axz 老兄。谢谢:)
【解决方案8】:

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

【讨论】:

  • 好的,这会使删除选项卡出现,但当您按下它时不会删除它。您需要删除数据源中的对象并重新加载表吗?
  • yes "// delete item at indexPath" 根据 indexPath 放置删除行的逻辑
  • 已在 iOS 14 中弃用。
【解决方案9】:

另外,这可以在 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)
    }
}

【讨论】:

    【解决方案10】:

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

    【讨论】:

      【解决方案11】:

      我知道这是个老问题,但 @Kurbz 的答案只需要 Xcode 6.3.2 和 SDK 8.3 的答案

      我需要添加[tableView beginUpdates][tableView endUpdates](感谢@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];
      }
      

      【讨论】:

        【解决方案12】:

        当您删除 tableview 的一个单元格时,您还必须删除索引 x 处的数组对象。

        我认为您可以使用滑动手势将其删除。 表格视图将调用委托:

        - (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 数组中删除对象,委托函数是不够的。

        希望我能帮到你。

        【解决方案13】:
        - (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 是单元格内容来自的数组
        【解决方案14】:

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

        【讨论】:

          【解决方案15】:

          如果您采用 diffable 数据源,则必须将委托回调移动到 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)
                      }
                  }
              }
          }
          

          【讨论】:

          • 能否请您详细说明如何在控制器中使用该类?
          【解决方案16】:

          斯威夫特 4,5

          要在滑动时删除单元格,有两个内置的 UITableView 方法。将此方法写入 TableView 数据源扩展中。

          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
          }
          

          【讨论】:

            【解决方案17】:

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

            【讨论】:

              【解决方案18】:

              对于 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);
                         }
              }
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2021-01-31
                • 1970-01-01
                • 2010-12-10
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多