【问题标题】:UITableView tableView(_:didEndEditingRowAt:) not called if rapidly tapping during swipe如果在滑动期间快速点击 UITableView tableView(_:didEndEditingRowAt:) 则不会调用
【发布时间】:2017-05-23 03:16:10
【问题描述】:

我有一个UITableView 和一个editAction,可以通过向左滑动访问。

似乎有一个内置的UITableView 功能,如果您滑动单元格以显示编辑操作,然后点击 tableView 中的任意位置,该操作会自动滑动关闭,并调用didEndEditingRowAt通知代理编辑结束。

但是,我看到的问题是,有时,如果您向左滑动,然后在滑动后非常快速地滑动(当只有一小部分编辑操作可见并且动画正在进行时),您点击任意位置否则在屏幕上,编辑操作已关闭,但不会调用 didEndEditingRowAt

因此,使用以下代码,我们最终使 tableView 处于编辑模式,但没有滑动打开任何视图,并且打印的最后一行是 Will Edit,确认从未调用过 didEndEditingRowAt

  class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {

  let tableView = UITableView()

  override func viewDidLoad() {
    super.viewDidLoad()

    view = tableView
    tableView.register(UITableViewCell.self, forCellReuseIdentifier: "foo")
    tableView.delegate = self
    tableView.dataSource = self
    tableView.allowsSelectionDuringEditing = false
  }

  func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
    let deleteAction = UITableViewRowAction(style: .normal, title: " Remove") {(_, indexPath) in print("OK") }
    return [deleteAction]
  }

  func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {}

  func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {}

  func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
    return indexPath
  }


  func tableView(_ tableView: UITableView, willBeginEditingRowAt indexPath: IndexPath) {
    print("Will edit")
  }

  func tableView(_ tableView: UITableView, didEndEditingRowAt indexPath: IndexPath?) {
    print("Did end edit")
  }

  func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    return 80
  }

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

  }

  func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    return tableView.dequeueReusableCell(withIdentifier: "foo") ?? UITableViewCell()
  }

  func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return 10
  }
}

现在,这种情况只是有时会发生,而且要把握好时机有点困难,但它绝对是可重现的。

这里是整个演示的链接:https://github.com/gregkerzhner/SwipeBugDemo

我在这里做错了什么或期待错了吗?在我的真实项目中,我的代码使其他单元格淡化以专注于当前正在编辑的单元格,并且我最终处于其他单元格淡化的糟糕状态,但聚焦的单元格没有打开任何编辑操作。

【问题讨论】:

  • 我很惊讶我能够在自己的项目中复制它,尽管只有十分之一的尝试。对我来说,这似乎是苹果公司的一个错误,我通常不愿意说。就个人而言,如果用户正常使用该应用程序,我认为他们不会故意这样做。
  • 是的,可能与openradar.me/19411256有关
  • 我想知道除了构建我自己的刷卡代码之外是否还有其他解决方法。否则这无法通过 QA!

标签: ios iphone swift uitableview


【解决方案1】:

这绝对是 UITableView 中的一个错误,即使滑动弹回(例如,如果你只滑动一点),它也会保持在“编辑”状态。

好消息是您可以使用 UITableViewCell 的一些方法来找到解决方法。 UITableViewCell 有相应的方法来通知动作相关的状态变化:

func willTransition(to state: UITableViewCellStateMask)
func didTransition(to state: UITableViewCellStateMask)
func setEditing(_ editing: Bool, animated: Bool)
var showingDeleteConfirmation: Bool { get }

在过渡(和动画)开始和结束时调用过渡方法。当你滑动时,willTransitiondidTransition 将被调用(状态为.showingDeleteConfirmationMask),showingDeleteConfirmation 将是truesetEditing 也被称为 true

虽然这仍然存在问题(除非您实际打开按钮,否则单元格不应成功编辑),didTransition 是一个回调,您有机会检查操作视图是否确实可见。我认为没有任何可靠的方法可以做到这一点,但也许只需检查单元格的contentView 占据了它的大部分边界就足够了。

【讨论】:

  • 我会接受这个作为答案,因为它似乎没有@ncke 的建议那么老套
  • @gregkerzhner 在你的边缘情况下,这些回调中的哪些成功通过了?恐怕他们会和 didEndEditing 进入同一个黑洞……
【解决方案2】:

所以最后,工作解决方案与@ncke 和@hybridcattt 建议的有点不同。

@ncke 解决方案的问题在于,在此滑动/点击交互过程中不会调用 func scrollViewDidEndDecelerating(_ scrollView: UIScrollView),因此永远不会调用解决方法。

@hybridcattt 的解决方案的问题是那些 UITableViewCell 回调被调用得太早,所以如果你执行轻扫快速点击动作,当所有这些回调得到时,UITableViewCellDeleteConfirmationView 仍然是单元格子视图的一部分调用。

最好的办法似乎是重写UITableViewCellwillRemoveSubview(_ subview: UIView) 函数。每次删除 UITableViewCellDeleteConfirmationView 时都会可靠地调用它,无论是在正常滑动关闭期间,还是在这种有问题的快速滑动场景中。

  protocol BugFixDelegate {
    func editingEnded(cell: UITableViewCell)
  }

  class CustomCell: UITableViewCell {
    weak var bugFixDelegate: BugFixDelegate?
    override func willRemoveSubview(_ subview: UIView) {
      guard String(describing: type(of: subview)) == "UITableViewCellDeleteConfirmationView" else {return }
      endEditing(true)
      bugFixDelegate.editingEnded(cell: self)
    }
  }

正如@hybridcattt 和@ncke 所建议的那样,在您的控制器中,您可以连接到这个委托并将丢失的事件发送到UITableViewUITableViewDelegate 就像

class DummyController: UIViewController {
  func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    guard let cell = tableView.dequeueReusableCell(withIdentifier: reuseIdentifier) as? CustomCell else {return UITableViewCell()}

    cell.bugFixDelegate = self

  }
}

extension DummyController: BugFixDelegate {
  //do all the missing stuff that was supposed to happen automatically
  func editingEnded(cell: UITableViewCell) {
    guard let indexPath = self.tableView.indexPath(for: cell) else {return}
    self.tableView.setEditing(false, animated: false)
    self.tableView.delegate?.tableView?(tableView, didEndEditingRowAt: indexPath)
  }
}

【讨论】:

  • 很好的提示,谢谢!就我而言,使用tableView.setEditing(false, animated: false) 结束对tableView 的编辑就足够了。这也叫tableView: didEndEditingRowAt indexPath:,一切都很好。
【解决方案3】:

我并不是说这是有史以来最好的事情,但如果您想要一个解决方法,这可能是一个开始:

extension UITableView {

    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {

        for cell in self.visibleCells {
            if cell.isEditing && (cell.subviews.count < 3 || cell.subviews[2].frame.origin.x < 30.0) {

                print("\(cell) is no longer editing")
                cell.endEditing(true)
                if let indexPath = self.indexPath(for: cell) {
                    self.delegate?.tableView?(self, didEndEditingRowAt: indexPath)
                }
            }
        }
    }

}

这个想法是 UITableView 是 UIScrollView 的子类。虽然前者的委托方法似乎坏了,但后者的仍在被调用。一些实验产生了这个测试。

只是一个想法 :) 您可能更喜欢继承 UITableView 而不是扩展,以本地化 hack。

【讨论】:

    【解决方案4】:

    这是我的解决方案。享受吧。

    func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCell.EditingStyle {
        if #available(iOS 10.0, *) {
        return .none;
        } else {
            return .delete;
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-03-07
      • 2012-09-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多