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

如何在滚动CollectionView内的TableView时隐藏导航栏

这个问题我之前做项目时也碰到过,核心难点就是嵌套层级里的滚动事件怎么传递到顶层的ViewController来控制导航栏。推荐用代理模式来做解耦,这是iOS开发里处理组件间通信的常规操作,下面一步步来实现:

步骤1:给CollectionViewCell定义代理协议

先创建一个协议,让Cell能把内部TableView的滚动事件传递出去:

protocol NestedTableViewScrollDelegate: AnyObject {
    func nestedTableViewDidScroll(_ scrollView: UIScrollView)
}

步骤2:实现CollectionViewCell,处理TableView滚动

在你的CollectionViewCell类里,把TableView的代理设为自己,然后在滚动回调里通知代理:

class CustomCollectionViewCell: UICollectionViewCell {
    // 假设你的TableView已经通过IB或者代码初始化完成
    @IBOutlet weak var nestedTableView: UITableView!
    
    weak var scrollDelegate: NestedTableViewScrollDelegate?
    
    override func awakeFromNib() {
        super.awakeFromNib()
        nestedTableView.delegate = self
        // 这里可以补充TableView的数据源配置、样式设置等
    }
}

// 扩展实现UITableViewDelegate(本质是UIScrollViewDelegate)
extension CustomCollectionViewCell: UITableViewDelegate {
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        // 把滚动事件传递给代理对象
        scrollDelegate?.nestedTableViewDidScroll(scrollView)
    }
}

步骤3:ViewController配置Cell代理并处理导航栏逻辑

在顶层ViewController里,实现CollectionView的数据源方法,同时承接Cell的代理事件,根据滚动方向控制导航栏显示/隐藏:

class MainViewController: UIViewController, UICollectionViewDataSource, NestedTableViewScrollDelegate {
    @IBOutlet weak var mainCollectionView: UICollectionView!
    
    // 记录上一次的滚动偏移量,用来判断滚动方向
    private var lastContentOffset: CGFloat = 0
    
    override func viewDidLoad() {
        super.viewDidLoad()
        mainCollectionView.dataSource = self
        // 关闭系统自带的导航栏自动隐藏,避免和自定义逻辑冲突
        navigationController?.hidesBarsOnSwipe = false
    }
    
    // MARK: - UICollectionViewDataSource
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 1 // 根据你的实际业务调整数量
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CustomCollectionViewCell", for: indexPath) as! CustomCollectionViewCell
        // 设置代理为当前ViewController
        cell.scrollDelegate = self
        return cell
    }
    
    // MARK: - NestedTableViewScrollDelegate
    func nestedTableViewDidScroll(_ scrollView: UIScrollView) {
        let currentOffset = scrollView.contentOffset.y
        // 判断滚动方向:向上滚动则隐藏导航栏,向下则显示
        let isScrollingUp = currentOffset > lastContentOffset
        
        // 加个阈值,避免轻微滚动就触发导航栏变化
        let threshold: CGFloat = 20
        if abs(currentOffset - lastContentOffset) > threshold {
            navigationController?.setNavigationBarHidden(isScrollingUp, animated: true)
        }
        
        // 更新上一次的偏移量
        lastContentOffset = currentOffset
    }
}

替代方案:用闭包简化事件传递

如果觉得代理写起来繁琐,也可以用闭包来实现,代码更简洁:
在CustomCollectionViewCell里添加闭包属性:

class CustomCollectionViewCell: UICollectionViewCell {
    @IBOutlet weak var nestedTableView: UITableView!
    
    var scrollHandler: ((UIScrollView) -> Void)?
    
    override func awakeFromNib() {
        super.awakeFromNib()
        nestedTableView.delegate = self
    }
}

extension CustomCollectionViewCell: UITableViewDelegate {
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        scrollHandler?(scrollView)
    }
}

然后在ViewController的Cell配置方法里设置闭包:

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CustomCollectionViewCell", for: indexPath) as! CustomCollectionViewCell
    cell.scrollHandler = { [weak self] scrollView in
        guard let self = self else { return }
        let currentOffset = scrollView.contentOffset.y
        let isScrollingUp = currentOffset > self.lastContentOffset
        let threshold: CGFloat = 20
        
        if abs(currentOffset - self.lastContentOffset) > threshold {
            self.navigationController?.setNavigationBarHidden(isScrollingUp, animated: true)
        }
        
        self.lastContentOffset = currentOffset
    }
    return cell
}

一些注意事项

  • 记得给CollectionViewCell和TableView设置正确的重用标识符,避免运行时崩溃。
  • 如果TableView有自定义的contentInset,可以用scrollView.adjustedContentInset来计算实际偏移量,保证判断逻辑准确。
  • 闭包方案里一定要用weak self,避免循环引用导致内存泄漏。
  • 如果项目里有多个嵌套的TableView,可以在代理方法里通过scrollView的实例区分不同的TableView,做针对性处理。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 06:33:53