使用Delegate&Protocol实现TableView按钮控制CollectionView显隐报错
问题场景
我尝试通过TableView单元格内的勾选按钮控制单元格内CollectionView的显示与隐藏,用Delegate和Protocol实现,但在按钮点击事件里调用代理方法时,出现了标题里的类型转换错误。我的代码步骤如下:
- 在TableViewCell类中定义协议:
protocol CustomCellDelegate{ func selectCollectionView(cell: InsideTableViewCell) }
- 在主ViewController中声明代理变量:
var delegate: CustomCellDelegate?
- 主ViewController遵循CustomCellDelegate协议:
class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource,UICollectionViewDelegate,UICollectionViewDataSource,CustomCellDelegate
- 在
cellForRowAtIndexPath方法中给单元格按钮添加点击事件并设置代理:
cell.checkButton.addTarget(self, action: #selector(self.selectCheck(_:)), for: .touchUpInside) cell.delegate = self
- 按钮点击的Selector函数:
@objc func selectCheck(_ sender: UIButton) { if sender.isSelected { sender.isSelected = false print("Check 1") delegate?.selectCollectionView(cell: InsideTableViewCell) // 这里报错! } else{ print("Check 2") sender.isSelected = true } }
- 实现的代理方法:
func selectCollectionView(cell: InsideTableViewCell) { cell.clCollectionView.isHidden = true }
错误原因分析
你遇到的报错核心问题是:在调用delegate?.selectCollectionView(cell: InsideTableViewCell)时,你传入的是InsideTableViewCell这个类的类型(Type),但代理方法需要的是这个类的一个具体实例对象(Instance),两者类型不匹配,所以编译器报错。
另外,你的代理设置逻辑还有个小问题:代理属性应该定义在InsideTableViewCell类中,而不是主ViewController里——因为是Cell需要通知VC做操作,所以Cell要持有代理(VC)的引用,而不是反过来。
修正步骤
1. 修正Cell类中的代理定义
首先,在InsideTableViewCell里正确定义协议和代理属性(注意要加weak避免循环引用):
// 协议要继承AnyObject,这样才能用weak修饰代理 protocol CustomCellDelegate: AnyObject { func selectCollectionView(cell: InsideTableViewCell) } class InsideTableViewCell: UITableViewCell { // 声明代理属性,必须是weak weak var delegate: CustomCellDelegate? // 你的控件:勾选按钮、CollectionView @IBOutlet weak var checkButton: UIButton! @IBOutlet weak var clCollectionView: UICollectionView! }
2. 移除ViewController中多余的代理变量
删掉主VC里的var delegate: CustomCellDelegate?——这个变量完全没必要,因为VC本身就是代理的实现者,Cell会持有VC的引用作为代理。
3. 修正按钮点击事件,传入正确的Cell实例
在selectCheck方法里,我们需要获取到当前点击按钮所在的那个InsideTableViewCell实例,而不是传类类型。可以通过递归查找父视图的方式,比直接用superview更可靠:
@objc func selectCheck(_ sender: UIButton) { // 切换按钮选中状态 sender.isSelected.toggle() // 打印日志 print(sender.isSelected ? "Check 2" : "Check 1") // 找到当前按钮所在的Cell实例 guard let cell = findParentCell(of: sender) else { return } // 调用代理方法,传入正确的Cell实例 cell.delegate?.selectCollectionView(cell: cell) } // 递归查找按钮所在的Cell private func findParentCell(of view: UIView) -> InsideTableViewCell? { if let cell = view as? InsideTableViewCell { return cell } return view.superview.flatMap { findParentCell(of: $0) } }
4. 优化代理方法的逻辑
让代理方法根据按钮的选中状态动态切换CollectionView的显示隐藏,而不是固定设为隐藏:
func selectCollectionView(cell: InsideTableViewCell) { // 按钮选中时显示CollectionView,未选中时隐藏 cell.clCollectionView.isHidden = !cell.checkButton.isSelected }
验证效果
现在运行代码,点击单元格的勾选按钮,就能正确触发代理方法,控制对应单元格内的CollectionView显示或隐藏了,也不会再出现类型转换的错误。
内容的提问来源于stack exchange,提问作者Vinayak Bhor

