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

SwiftUI中不使用List实现ScrollView粘性头部的方案求助

实现非首元素的粘性头部(View B固定在导航栏下方)

要实现这个需求,我们可以通过GeometryReader监听View B的滚动位置,结合状态变量控制一个叠加在ScrollView上方的View B副本的显示/隐藏,同时让原View B随滚动移动,从而达到“上滑固定、下滑归位”的效果,且保证固定后的View B始终在滚动内容上方。

完整实现代码

struct TestScreen: View {
    // 记录View B与导航栏底部的偏移差,用于判断是否触发固定
    @State private var trackViewBOffset: CGFloat = 0
    // Inline模式下导航栏的默认高度
    private let navigationBarHeight: CGFloat = 44
    
    var body: some View {
        NavigationStack {
            ZStack(alignment: .top) {
                ScrollView {
                    VStack {
                        Text("View A")
                            .frame(height: 50)
                            .frame(maxWidth: .infinity)
                            .background(.green)
                        
                        // 给View B添加位置监听
                        Text("View B - Sticky")
                            .frame(height: 50)
                            .frame(maxWidth: .infinity)
                            .background(.blue)
                            .background(
                                GeometryReader { geo in
                                    Color.clear
                                        .onChange(of: geo.frame(in: .global).minY) { newTopY in
                                            // 计算View B顶部到导航栏底部的距离
                                            trackViewBOffset = newTopY - navigationBarHeight
                                        }
                                }
                            )
                        
                        ForEach(0..<15) { i in
                            Text("View \(i)")
                                .frame(height: 50)
                                .frame(maxWidth: .infinity)
                                .background(Color.red)
                        }
                    }
                }
                .navigationBarTitleDisplayMode(.inline)
                .toolbarBackground(.clear, for: .navigationBar)
                .toolbar {
                    ToolbarItem(placement: .principal) {
                        Text("Testing")
                            .foregroundColor(.purple)
                    }
                }
                
                // 固定在导航栏下方的View B副本
                if trackViewBOffset <= 0 {
                    Text("View B - Sticky")
                        .frame(height: 50)
                        .frame(maxWidth: .infinity)
                        .background(.blue)
                        .offset(y: navigationBarHeight)
                }
            }
        }
    }
}

关键细节说明

  • 位置监听逻辑:通过GeometryReader获取View B在全局坐标系中的顶部位置,计算它与导航栏底部的差值。当差值≤0时,说明View B已滚动到导航栏下方,此时显示顶层的固定副本。
  • 分层显示机制:ZStack将固定的View B副本放在ScrollView上方,仅在需要固定时显示,原View B随内容正常滚动,视觉上实现“粘性”效果。
  • 导航栏适配:Inline模式下导航栏高度固定为44,若需适配动态高度,可通过UIToolbar.appearance().frame.height获取实际值替换固定常量。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 05:42:38