UICollectionViewCell嵌套UITableView:滚动保留+非可选行触摸透传需求
可行实现方案
核心思路是区分处理滚动与点击事件:让UITableView保留滚动能力,同时在点击不可选行时,不拦截事件,让其向上透传给外层UICollectionView。具体实现如下:
1. 自定义UITableView拦截点击手势
自定义一个继承自UITableView的子类,通过重写gestureRecognizerShouldBegin(_:)方法,对点击手势(UITapGestureRecognizer)做针对性拦截:
class NestedTableView: UITableView { // 闭包:传入indexPath,返回对应行是否可选 var isRowSelectable: ((IndexPath) -> Bool)? override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) { // 仅处理点击手势,滚动手势(如UIPan)正常放行 guard let tapGesture = gestureRecognizer as? UITapGestureRecognizer else { return super.gestureRecognizerShouldBegin(gestureRecognizer) } // 获取点击位置对应的行 let tapPos = tapGesture.location(in: self) if let indexPath = indexPathForRow(at: tapPos) { // 如果该行不可选,阻止点击手势生效,事件会向上传递给外层CollectionView if let selectable = isRowSelectable?(indexPath), !selectable { return false } } return super.gestureRecognizerShouldBegin(gestureRecognizer) } }
2. 在CollectionViewCell中配置自定义TableView
将CollectionViewCell中的普通UITableView替换为上面的自定义类,并绑定数据与可选性判断逻辑:
class CustomCollectionCell: UICollectionViewCell { private let tableView = NestedTableView() var dataList: [RowDataModel] = [] { didSet { tableView.reloadData() } } override init(frame: CGRect) { super.init(frame: frame) setupTableView() } required init?(coder: NSCoder) { super.init(coder: coder) setupTableView() } private func setupTableView() { tableView.translatesAutoresizingMaskIntoConstraints = false contentView.addSubview(tableView) NSLayoutConstraint.activate([ tableView.topAnchor.constraint(equalTo: contentView.topAnchor), tableView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), tableView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), tableView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor) ]) tableView.dataSource = self tableView.delegate = self // 绑定行可选性判断逻辑 tableView.isRowSelectable = { [weak self] indexPath in guard let self = self else { return false } return self.dataList[indexPath.row].isSelectable } } } // UITableView数据源与代理实现 extension CustomCollectionCell: UITableViewDataSource, UITableViewDelegate { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return dataList.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "RowCell", for: indexPath) cell.textLabel?.text = dataList[indexPath.row].title return cell } // 双重保险:确保不可选行不会被选中 func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? { return dataList[indexPath.row].isSelectable ? indexPath : nil } }
方案说明
- 滚动能力保留:仅拦截点击手势,滚动相关的
UIPanGestureRecognizer不受影响,TableView始终可以垂直滚动。 - 行可选性配置:通过
isRowSelectable闭包,可根据数据模型动态控制每一行是否可选。 - 事件透传实现:当点击不可选行时,TableView放弃处理点击手势,事件会沿着响应链向上传递,最终被外层UICollectionView捕获,触发其代理方法(如
collectionView(_:didSelectItemAt:))。
注意:确保
UICollectionViewCell和NestedTableView的isUserInteractionEnabled属性保持默认的true,否则会影响事件传递。
内容的提问来源于stack exchange,提问作者D. Mika
相关产品推荐
相关产品推荐

