嵌套TableView中选中新内部单元格时,如何取消原选中单元格?
嘿,这个嵌套TableView的选中状态问题我之前踩过坑!每个内部TableView都是独立的实例,互相不知道对方的选中情况,所以单独在自定义Cell里调用deselectRow肯定没用——因为你找不到之前那个选中的cell对应的内部TableView对吧?给你两个实用的解决方案,按需选:
方案一:用闭包传递选中事件,全局记录选中状态
这个方案适合简单场景,代码量少,逻辑清晰:
首先在你的外部ViewController(就是持有MainTableView的那个控制器)里,定义两个变量保存上一次选中的位置信息:
// 记录上一次选中的内部TableView所在的外部Cell的IndexPath private var lastSelectedOuterIndexPath: IndexPath? // 记录上一次选中的内部TableView单元格的IndexPath private var lastSelectedInnerIndexPath: IndexPath?
然后给你的自定义TableViewCell加一个闭包,用来把内部单元格的选中事件传递给外部控制器:
class CustomTableViewCell: UITableViewCell { @IBOutlet weak var innerTableView: UITableView! // 传递内部选中事件的闭包 var onInnerCellSelected: ((IndexPath) -> Void)? override func awakeFromNib() { super.awakeFromNib() innerTableView.delegate = self innerTableView.dataSource = self innerTableView.allowsSelection = true } // 处理Cell复用,避免选中状态错乱 override func prepareForReuse() { super.prepareForReuse() if let selectedPath = innerTableView.indexPathForSelectedRow { innerTableView.deselectRow(at: selectedPath, animated: false) } } } extension CustomTableViewCell: UITableViewDelegate, UITableViewDataSource { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { // 触发闭包,把内部选中的IndexPath传出去 onInnerCellSelected?(indexPath) } // 这里实现你的内部TableView的dataSource方法(比如numberOfRows等) // ... }
接下来在外部ViewController的cellForRowAt方法里,给每个自定义Cell绑定闭包,同时处理选中状态的更新:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCellID", for: indexPath) as! CustomTableViewCell // 绑定闭包,处理内部选中事件 cell.onInnerCellSelected = { [weak self] innerIndexPath in guard let self = self else { return } // 第一步:取消上一次的选中状态 if let lastOuterPath = self.lastSelectedOuterIndexPath, let lastInnerPath = self.lastSelectedInnerIndexPath { // 找到上一次的外部Cell if let lastCell = tableView.cellForRow(at: lastOuterPath) as? CustomTableViewCell { // 取消内部TableView的选中 lastCell.innerTableView.deselectRow(at: lastInnerPath, animated: true) } } // 第二步:记录当前选中的位置 self.lastSelectedOuterIndexPath = indexPath self.lastSelectedInnerIndexPath = innerIndexPath } return cell }
方案二:用代理模式统一管理(适合复杂项目)
如果你的项目结构比较复杂,用代理模式会更规范,耦合度更低:
首先定义一个代理协议:
protocol InnerTableViewSelectionDelegate: AnyObject { func innerTableView(_ innerTableView: UITableView, didSelectRowAt indexPath: IndexPath, in outerCellIndexPath: IndexPath) }
然后在自定义Cell里添加代理属性和外部Cell的IndexPath记录:
class CustomTableViewCell: UITableViewCell { @IBOutlet weak var innerTableView: UITableView! weak var selectionDelegate: InnerTableViewSelectionDelegate? // 保存当前外部Cell的IndexPath,由外部控制器设置 var outerCellIndexPath: IndexPath! override func awakeFromNib() { super.awakeFromNib() innerTableView.delegate = self innerTableView.dataSource = self } override func prepareForReuse() { super.prepareForReuse() if let selectedPath = innerTableView.indexPathForSelectedRow { innerTableView.deselectRow(at: selectedPath, animated: false) } } } extension CustomTableViewCell: UITableViewDelegate { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { // 通知代理,传递内部选中的位置和外部Cell的位置 selectionDelegate?.innerTableView(tableView, didSelectRowAt: indexPath, in: outerCellIndexPath) } }
最后让外部ViewController遵守这个代理,实现代理方法:
class MainViewController: UIViewController, InnerTableViewSelectionDelegate { private var lastSelectedOuterIndexPath: IndexPath? private var lastSelectedInnerIndexPath: IndexPath? func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCellID", for: indexPath) as! CustomTableViewCell cell.selectionDelegate = self cell.outerCellIndexPath = indexPath return cell } func innerTableView(_ innerTableView: UITableView, didSelectRowAt indexPath: IndexPath, in outerCellIndexPath: IndexPath) { // 取消上一次选中 if let lastOuter = lastSelectedOuterIndexPath, let lastInner = lastSelectedInnerIndexPath { if let lastCell = MainTableView.cellForRow(at: lastOuter) as? CustomTableViewCell { lastCell.innerTableView.deselectRow(at: lastInner, animated: true) } } // 更新选中记录 lastSelectedOuterIndexPath = outerCellIndexPath lastSelectedInnerIndexPath = indexPath } }
这两个方案的核心思路都是把选中状态的管理权交给外部控制器,因为只有外部控制器能访问到所有的外部Cell和它们的内部TableView,这样就能轻松找到上一次选中的单元格并取消它的选中状态啦!
内容的提问来源于stack exchange,提问作者SM Abu Taher Asif
相关产品推荐
相关产品推荐

