You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

嵌套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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:32:14