如何在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
相关产品推荐
相关产品推荐

