【问题标题】:UIContextualAction with destructive style seems to delete row by default具有破坏性样式的 UIContextualAction 似乎默认删除行
【发布时间】:2017-11-04 00:07:14
【问题描述】:

我看到的问题是,当我用.destructive 创建UIContextualAction 并在completionHandler 中传递true 时,似乎有一个删除行的默认操作。

如果您从 Xcode 的模板创建一个新的 Master-Detail App 并将此代码添加到 MasterViewController...

override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
    let testAction = UIContextualAction(style: .destructive, title: "Test") { (_, _, completionHandler) in
        print("test")
        completionHandler(true)
    }
    return UISwipeActionsConfiguration(actions: [testAction])
}

您滑动的行将被删除。请注意,那里没有更新表格视图的代码。此外,模型没有更新,如果您向上滚动以导致重新加载该行,它将重新出现。

在这种情况下传递false 不会删除该行。或者使用.normal 样式和true 也不会删除该行。

.destructive 和 true 导致默认删除该行。

谁能解释这种行为?为什么要删除该行?

【问题讨论】:

    标签: swift uitableview swipe ios11


    【解决方案1】:

    根据 Destructive 选项的文档:

    删除数据或执行某种破坏性任务的操作。

    完成是为了表示操作是否成功。传递 true 意味着破坏性任务是成功的,因此应该删除该行。

    当破坏性操作发生时,您应该手动更新您的数据源,如果不这样做会导致滚动以使数据重新出现。您还需要告诉 tableView 数据已被删除。

    下面是一些显示工作示例的代码:

    UIContextualAction(style: .destructive, title: "Delete") { [weak self] (_, _, completion) in
        if self?.canDelete(indexPath) { // We can actually delete
            // remove the object from the data source
            self?.myData.remove(at: indexPath.row)
            // delete the row.  Without deleting the row or reloading the
            // tableview, the index will be off in future swipes
            self?.tableView?.deleteRows(at: [indexPath], with: .none)
            // Let the action know it was a success.  In this case the 
            // tableview will animate the cell removal with the swipe
            completion(true)    
        } else { // We can't delete for some reason
            // This resets the swipe state and nothing is removed from
            // the screen visually.
            completion(false)
        }         
    }
    

    然后我需要重新加载tableview 或调用deleteRows 以便在下一次滑动时正确计算indexPath。

    如果我有 10 行并且我刷了第 5 行以删除,那么之后的每一行都会关闭一行,除非重新加载 tableview 或告知 tableview 以某种方式删除了该行。

    【讨论】:

    • 在此代码之后,我遇到了崩溃。原因是——你不应该在这一行 self?.tableView?.deleteRows(at: [indexPath], with: .none) 中自己删除行。当您调用 completion(true) 时,UIKit 会执行删除行
    【解决方案2】:

    我无法在 iOS 13 中重现此问题。iOS 12 及之前版本中的行为是一个错误,或者它已被撤回(可能是因为它令人困惑)。

    在 iOS 13 中,如果您只是通过调用 completion(true) 从 .destructive 操作返回而没有执行任何操作,则不会发生任何事情,这就是问题的结束。该单元格没有为您删除。

    【讨论】:

    【解决方案3】:

    我同意Kris Gellci 的回答,但请注意,如果您使用的是 NSFetchedResultsController,它可能会使事情复杂化。似乎对于破坏性 UIContextualAction 调用 completion(true) 将删除该行,但 NSFetchedResultsController 的委托也可能如此。所以你很容易以这种方式结束错误。使用 NSFetchedResultsController 我决定调用completion(false)(关闭上下文菜单),无论操作是否成功,然后如果相应的对象已被删除,则让委托负责删除表行。

    【讨论】:

      【解决方案4】:

      使用下面的标志来禁用完全滑动。

      执行FirstActionWithFullSwipe

      默认情况下,它会自动执行在 UISwipeActionsConfiguration 中配置的第一个动作。因此,如果您想禁用完全滑动删除,请将“performsFirstActionWithFullSwipe”设置为 false。

      func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
          let test = UIContextualAction(style: .destructive, title: "test") { (action, view, completion) in
              
              // Your Logic here
              completion(true)
          }
          
          let config = UISwipeActionsConfiguration(actions: [test])
          config.performsFirstActionWithFullSwipe = false
          return config
      } 
      

      希望这能解决您的问题。

      【讨论】:

        猜你喜欢
        • 2019-03-09
        • 1970-01-01
        • 1970-01-01
        • 2014-04-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多