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

如何实现UITableView两个头部视图的吸顶效果

实现双头部吸顶(折叠顶部Header + 固定Section头部)

核心修改方案

针对你需要的「顶部Header折叠后吸顶、Section Header固定吸顶」效果,分两部分调整代码:


1. 完善顶部折叠Header的吸顶逻辑

修改HeaderView.swift中的updatePosition方法,确保Header折叠到最小高度后固定在安全区顶部:

func updatePosition() {
    guard let scrollView = scrollView else { return }
    
    let minimumSize = minimumHeight
    let referenceOffset = scrollView.safeAreaInsets.top
    let initialHeaderHeight: CGFloat = 250 // 和tableView contentInset的top值保持一致
    
    let scrollOffsetY = scrollView.contentOffset.y
    let maxCollapseOffset = initialHeaderHeight - minimumSize
    
    // 滚动超过最大折叠偏移量时,Header保持最小高度并固定
    if scrollOffsetY >= maxCollapseOffset {
        frame = CGRect(x: 0, y: referenceOffset, width: scrollView.frame.width, height: minimumSize)
    } else {
        // 未完全折叠时,动态调整高度和位置
        let currentHeight = initialHeaderHeight - scrollOffsetY
        frame = CGRect(x: 0, y: referenceOffset + scrollOffsetY, width: scrollView.frame.width, height: currentHeight)
    }
}

同时在HomeTableViewController中添加滚动监听,实时更新Header位置:

override func scrollViewDidScroll(_ scrollView: UIScrollView) {
    headerView.updatePosition()
}

2. 实现Section Header固定吸顶

将原tableView的section header改为独立视图,通过滚动偏移控制其位置:

步骤1:添加自定义Section Header属性

在HomeTableViewController中声明:

private lazy var sectionHeaderView: UIView = {
    let view = UIView()
    view.backgroundColor = .systemBackground
    view.translatesAutoresizingMaskIntoConstraints = false
    
    // 标题Label
    let titleLabel = UILabel()
    titleLabel.text = "Subtitle"
    titleLabel.textColor = .label
    titleLabel.font = UIFont.systemFont(ofSize: 20.0, weight: .semibold)
    titleLabel.translatesAutoresizingMaskIntoConstraints = false
    view.addSubview(titleLabel)
    
    // 按钮
    let showAllButton = UIButton(type: .system)
    showAllButton.setTitle("alle anzeigen →", for: .normal)
    showAllButton.setTitleColor(.secondaryLabel, for: .normal)
    showAllButton.titleLabel?.font = UIFont.systemFont(ofSize: 15.0, weight: .semibold)
    showAllButton.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
    showAllButton.translatesAutoresizingMaskIntoConstraints = false
    view.addSubview(showAllButton)
    
    // 布局约束
    NSLayoutConstraint.activate([
        titleLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: view.frame.width * 0.05),
        titleLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor),
        
        showAllButton.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -view.frame.width * 0.05),
        showAllButton.centerYAnchor.constraint(equalTo: view.centerYAnchor),
        
        view.heightAnchor.constraint(equalToConstant: 58)
    ])
    
    return view
}()

步骤2:初始化并隐藏原Section Header

在viewDidLoad中添加视图并隐藏tableView默认section header:

override func viewDidLoad() {
    super.viewDidLoad()
    
    // ... 原有代码 ...
    
    // 添加自定义section header到视图
    view.addSubview(sectionHeaderView)
    NSLayoutConstraint.activate([
        sectionHeaderView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
        sectionHeaderView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
        sectionHeaderView.heightAnchor.constraint(equalToConstant: 58)
    ])
    
    // 隐藏tableView默认section header
    tableView.sectionHeaderHeight = 0
}

步骤3:滚动时更新Section Header位置

修改scrollViewDidScroll方法,让Section Header在顶部Header完全折叠后固定:

override func scrollViewDidScroll(_ scrollView: UIScrollView) {
    headerView.updatePosition()
    
    let headerMinHeight = headerView.minimumHeight
    let safeTop = scrollView.safeAreaInsets.top
    let maxCollapseOffset = 250 - headerMinHeight // 250为顶部Header初始高度
    
    // 顶部Header完全折叠后,Section Header固定在其下方;否则跟随滚动
    if scrollView.contentOffset.y >= maxCollapseOffset {
        sectionHeaderView.frame.origin.y = safeTop + headerMinHeight
    } else {
        sectionHeaderView.frame.origin.y = safeTop + 250 + scrollView.contentOffset.y
    }
}

步骤4:适配安全区变化

更新viewSafeAreaInsetsDidChange和viewWillLayoutSubviews,确保位置正确:

override func viewSafeAreaInsetsDidChange() {
    super.viewSafeAreaInsetsDidChange()
    tableView.contentInset = UIEdgeInsets(top: 250 + tableView.safeAreaInsets.top, left: 0, bottom: 0, right: 0)
    headerView.updatePosition()
    scrollViewDidScroll(tableView)
}

override func viewWillLayoutSubviews() {
    super.viewWillLayoutSubviews()
    headerView.updatePosition()
    scrollViewDidScroll(tableView)
}

3. 调整TableView内容间距

避免内容和自定义Section Header重叠,设置section间距:

override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
    return 0
}

override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
    return .leastNormalMagnitude
}

最终效果

  • 向上滚动:顶部Header逐步折叠,达到最小高度后固定在安全区顶部;Section Header随滚动上移,直到顶部Header完全折叠后,固定在其下方
  • 向下滚动:顶部Header逐步展开,Section Header跟随回到初始位置

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 20:12:03