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

如何在List的Text视图开始滑入导航栏下方时触发操作?

实现列表项开始被导航栏遮挡时触发操作

要精准捕获列表项开始滑入导航栏下方的时刻,你可以通过PreferenceKey + GeometryReader的组合方案实现,完全规避onDisappear的两个问题。以下是具体实现:

步骤1:定义PreferenceKey捕获列表项位置

首先创建一个PreferenceKey,用来收集每个列表项在导航栈坐标系中的位置信息:

struct ItemFramePreferenceKey: PreferenceKey {
    static var defaultValue: [Int: CGRect] = [:]
    
    static func reduce(value: inout [Int: CGRect], nextValue: () -> [Int: CGRect]) {
        value.merge(nextValue()) { $1 }
    }
}

步骤2:完整视图实现

在视图中动态获取导航栏高度,结合PreferenceKey检测列表项位置,只在目标时刻触发操作:

struct DemoView: View {
    @State private var navigationBarHeight: CGFloat = 0
    @State private var triggeredItems: Set<Int> = [] // 避免重复触发
    
    var body: some View {
        NavigationStack {
            List {
                ForEach(0..<50, id: \.self) { value in
                    Text(value.formatted())
                        .background(
                            // 捕获当前列表项在导航栈坐标系中的位置
                            GeometryReader { geo in
                                Color.clear
                                    .preference(
                                        key: ItemFramePreferenceKey.self,
                                        value: [value: geo.frame(in: .named("navigationStack"))]
                                    )
                            }
                        )
                }
            }
            .navigationTitle("Demo view")
            .coordinateSpace(name: "navigationStack") // 定义统一坐标系
            .background(
                // 动态获取导航栏(含安全区)的高度
                GeometryReader { geo in
                    Color.clear
                        .onAppear {
                            navigationBarHeight = geo.safeAreaInsets.top
                        }
                        .onChange(of: geo.safeAreaInsets.top) { newHeight in
                            navigationBarHeight = newHeight
                        }
                }
            )
            .onPreferenceChange(ItemFramePreferenceKey.self) { frames in
                for (value, frame) in frames {
                    // 判断条件:列表项顶部进入导航栏区域,且未触发过,且未从底部消失
                    if frame.minY < navigationBarHeight && frame.maxY > 0 && !triggeredItems.contains(value) {
                        debugPrint("Item \(value) 开始被导航栏遮挡")
                        triggeredItems.insert(value)
                    }
                }
            }
        }
    }
}

方案说明

  • 精准触发时机:通过frame.minY < navigationBarHeight判断列表项顶部刚进入导航栏区域的瞬间,这正是你需要的"开始滑入导航栏下方"的时刻,而非完全消失时。
  • 排除底部消失场景:frame.maxY > 0确保只处理顶部被遮挡的列表项,从底部消失的项会被过滤掉。
  • 避免重复触发:用triggeredItems集合记录已触发的项,防止滚动过程中重复执行操作。
  • 适配动态导航栏:通过safeAreaInsets.top动态获取导航栏高度,适配大标题/小标题切换、不同设备的安全区差异。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 18:02:28