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

子视图UITableView滚动触发父视图cellForRowAt问题及解决

问题描述

我有两个ViewController,每个视图中都包含UITableView并已设置代理和数据源。当在第二个视图中向上滚动UITableView时,父视图的cellForRowAt方法被调用,但numberOfRowsInSection方法不会触发。我尝试修改UITableView名称后问题仍然存在,且必须以卡片样式(非.fullScreen)模态展示第二个ViewController。请问该现象的原因是什么?如何解决?

相关代码

第一个ViewController代码

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    @IBOutlet weak var dataTable: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()
    
        dataTable.delegate = self
        dataTable.dataSource = self
    }

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

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = UITableViewCell(style: .default, reuseIdentifier: "firstCell")
        cell.textLabel?.text = String(indexPath.row)
    
        return cell
    }
}

跳转至SecondViewController的代码

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    let secondSB = UIStoryboard(name: "Second", bundle: nil)
    let secondVC = secondSB.instantiateViewController(withIdentifier: "second")
    let nc = UINavigationController(rootViewController: secondVC)
    nc.modalPresentationStyle = .automatic
    
    present(nc, animated: true)
}

第二个ViewController代码

class SecondVC: UIViewController , UITableViewDelegate, UITableViewDataSource {

    @IBOutlet weak var dataTable: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()
    
        dataTable.delegate = self
        dataTable.dataSource = self
    }

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

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = UITableViewCell(style: .default, reuseIdentifier: "firstCell")
        cell.textLabel?.text = "Second" +  String(indexPath.row)
    
        return cell
    }

}
原因分析

在iOS 13及以上版本,卡片式模态(.pageSheet或.automatic默认的卡片样式)展示ViewController时,父视图控制器不会被完全从视图层级中移除,仍存在于底层。由于两个ViewController都实现了UITableViewDelegate和UITableViewDataSource协议,且代理方法未对传入的tableView参数做实例判断,当第二个页面的TableView滚动时,系统响应链可能误触发父视图的cellForRowAt方法。而numberOfRowsInSection不会被触发,是因为第二个TableView已有自身数据源返回行数,无需调用父视图的对应方法。

解决方法

在所有UITableView的代理/数据源方法中,先判断传入的tableView是否为当前ViewController持有的实例,不是则返回默认值或跳过处理。

修改后的第一个ViewController代码

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    @IBOutlet weak var dataTable: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()
    
        dataTable.delegate = self
        dataTable.dataSource = self
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        guard tableView === self.dataTable else {
            return 0
        }
        return 20
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        guard tableView === self.dataTable else {
            return UITableViewCell()
        }
        let cell = UITableViewCell(style: .default, reuseIdentifier: "firstCell")
        cell.textLabel?.text = String(indexPath.row)
    
        return cell
    }
}

修改后的第二个ViewController代码

class SecondVC: UIViewController , UITableViewDelegate, UITableViewDataSource {

    @IBOutlet weak var dataTable: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()
    
        dataTable.delegate = self
        dataTable.dataSource = self
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        guard tableView === self.dataTable else {
            return 0
        }
        return 10
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        guard tableView === self.dataTable else {
            return UITableViewCell()
        }
        let cell = UITableViewCell(style: .default, reuseIdentifier: "firstCell")
        cell.textLabel?.text = "Second" +  String(indexPath.row)
    
        return cell
    }

}

通过这种方式,可确保每个ViewController只处理自身持有的TableView的代理/数据源请求,避免跨页面的方法误触发。

内容的提问来源于stack exchange,提问作者Eric

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 18:53:32