Swift 4中调用popViewController无响应:点击UITableViewCell返回UIView求助
解决UITableView点击返回并传值的问题
嘿,作为Swift新手遇到这种问题太正常啦,我来帮你一步步捋清楚问题所在,然后搞定它~
首先,你说调用navigationController?.popViewController(animated: true)没效果,大概率是跳转方式的问题,先搞清楚这个核心问题:
为什么pop没效果?
你提到B已经嵌入UINavigationController,并且用show segue从A跳转到B。这里要注意:
如果A本身没有嵌入UINavigationController,那show segue其实是把B的导航控制器以**模态(present)**的方式展示出来的,而不是push到导航栈里。这时候B所在的导航栈里只有它自己,popViewController自然没地方可返回。
你可以在B的viewDidLoad里加一行代码验证:
print(navigationController?.viewControllers ?? [])
如果输出结果里只有B这一个控制器,就说明确实是模态跳转的,这时候你应该用dismiss代替pop:
dismiss(animated: true, completion: nil)
如果想要用pop正常返回,那需要调整跳转逻辑:让A也嵌入UINavigationController,然后直接用show(或push)segue跳转到B本身(而不是跳转到B的导航控制器),这样A和B就处于同一个导航栈里,pop就能顺利回到A了。
搞定数据传递
不管是用pop还是dismiss,要把B的单元格数据传给A,最常用的两种方式是代理模式和闭包回调,我都给你写清楚:
方式一:代理模式(iOS反向传值标准做法)
- 在B控制器里定义一个代理协议:
// 替换YourDataType为你实际的数据类型,比如String、自定义Model等 protocol TableViewControllerBDelegate: AnyObject { func didSelectCell(with data: YourDataType) }
- 在B里声明一个弱引用的代理属性:
weak var delegate: TableViewControllerBDelegate?
- 在单元格点击方法里,先调用代理传递数据,再返回:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { // 拿到选中单元格的数据 let selectedData = yourDataArray[indexPath.row] // 通知代理(也就是A控制器)拿到数据 delegate?.didSelectCell(with: selectedData) // 根据跳转方式选择返回方法 navigationController?.popViewController(animated: true) // 如果是模态跳转就用下面这句 // dismiss(animated: true, completion: nil) }
- 在A控制器里,遵守代理协议并实现方法:
extension ViewControllerA: TableViewControllerBDelegate { func didSelectCell(with data: YourDataType) { // 在这里处理传递过来的数据,比如更新UI、保存到变量等 print("从B拿到的数据:\(data)") } }
- 在A的
prepare(for segue:...)方法里设置代理:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) { // 记得给segue设置一个唯一的identifier,比如"GoToViewControllerB" if segue.identifier == "GoToViewControllerB" { var targetVC: TableViewControllerB? // 如果是直接跳转到B if let vc = segue.destination as? TableViewControllerB { targetVC = vc } // 如果是跳转到B的导航控制器,要先拿到导航的顶层控制器 else if let navVC = segue.destination as? UINavigationController { targetVC = navVC.topViewController as? TableViewControllerB } // 设置代理 targetVC?.delegate = self } }
方式二:闭包回调(更简洁的写法)
- 在B控制器里声明一个闭包属性:
var onDataSelected: ((YourDataType) -> Void)?
- 在单元格点击方法里触发闭包并返回:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { let selectedData = yourDataArray[indexPath.row] // 触发闭包传递数据 onDataSelected?(selectedData) // 选择对应的返回方法 navigationController?.popViewController(animated: true) // dismiss(animated: true, completion: nil) }
- 在A的
prepare(for segue:...)里设置闭包:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if segue.identifier == "GoToViewControllerB" { var targetVC: TableViewControllerB? if let vc = segue.destination as? TableViewControllerB { targetVC = vc } else if let navVC = segue.destination as? UINavigationController { targetVC = navVC.topViewController as? TableViewControllerB } // 用weak self避免循环引用 targetVC?.onDataSelected = { [weak self] data in guard let self = self else { return } // 处理数据 print("从B拿到的数据:\(data)") } } }
最后再检查一遍
- 确认跳转方式和返回方法匹配:push用pop,present用dismiss。
- 给segue设置正确的identifier,避免prepare方法里找不到目标控制器。
- 数据类型要对应,别把String传给Int类型的参数哦~
内容的提问来源于stack exchange,提问作者Chris
相关产品推荐
相关产品推荐

