如果在滑动期间快速点击 UITableView tableView(_:didEndEditingRowAt:) 则不会调用
Posted
技术标签:
【中文标题】如果在滑动期间快速点击 UITableView tableView(_:didEndEditingRowAt:) 则不会调用【英文标题】:UITableView tableView(_:didEndEditingRowAt:) not called if rapidly tapping during swipe 【发布时间】: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! 【参考方案1】:这绝对是 UITableView 中的一个错误,即使滑动弹回(例如,如果你只滑动一点),它也会保持在“编辑”状态。
好消息是您可以使用 UITableViewCell 的一些方法来找到解决方法。 UITableViewCell 有相应的方法来通知动作相关的状态变化:
func willTransition(to state: UITableViewCellStateMask)
func didTransition(to state: UITableViewCellStateMask)
func setEditing(_ editing: Bool, animated: Bool)
var showingDeleteConfirmation: Bool get
在过渡(和动画)开始和结束时调用过渡方法。当你滑动时,willTransition
和 didTransition
将被调用(状态为.showingDeleteConfirmationMask
),showingDeleteConfirmation
将是true
。 setEditing
也被称为 true
。
虽然这仍然存在问题(除非您实际打开按钮,否则单元格不应成功编辑),didTransition
是一个回调,您有机会检查操作视图是否确实可见。我认为没有任何可靠的方法可以做到这一点,但也许只需检查单元格的contentView
占据了它的大部分边界就足够了。
【讨论】:
我会接受这个作为答案,因为它似乎没有@ncke 的建议那么老套 @gregkerzhner 在你的边缘情况下,这些回调中的哪些成功通过了?恐怕他们会和 didEndEditing 进入同一个黑洞……【参考方案2】:所以最后,工作解决方案与@ncke 和@hybridcattt 建议的有点不同。
@ncke 解决方案的问题在于,在此滑动/点击交互过程中不会调用 func scrollViewDidEndDecelerating(_ scrollView: UIScrollView)
,因此永远不会调用解决方法。
@hybridcattt 的解决方案的问题是那些 UITableViewCell
回调被调用得太早,所以如果你执行轻扫快速点击动作,当所有这些回调得到时,UITableViewCellDeleteConfirmationView
仍然是单元格子视图的一部分调用。
最好的办法似乎是重写UITableViewCell
的willRemoveSubview(_ 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 所建议的那样,在您的控制器中,您可以连接到这个委托并将丢失的事件发送到UITableView
和UITableViewDelegate
就像
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;
【讨论】:
以上是关于如果在滑动期间快速点击 UITableView tableView(_:didEndEditingRowAt:) 则不会调用的主要内容,如果未能解决你的问题,请参考以下文章
iOS UITableView横向滑动点击UIButton放大
试图使 UITableViewCell 可滑动和可点击,同时保持 UITableView 可滚动,为啥我的单元格滑动有时只能工作?