如何判断SwiftUI List是否位于顶部位置或已发生滚动?
SwiftUI List 顶部位置检测实现方案
要实现仅在列表处于顶部时才对新增元素添加动画,我们可以通过PreferenceKey监听列表滚动偏移量,判断列表当前是否位于顶部。
实现步骤
- 定义用于传递滚动偏移量的
PreferenceKey - 在列表顶部插入透明占位视图,读取自身在滚动坐标系下的偏移值
- 监听偏移值变更,更新列表是否处于顶部的状态
- 把动画的生效条件绑定到顶部状态即可
完整代码示例
import SwiftUI // 定义滚动偏移量监听用的PreferenceKey private struct ScrollOffsetPreferenceKey: PreferenceKey { static var defaultValue: CGFloat = 0 static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) { value = nextValue() } } struct ListAnimationDemoView: View { // 列表数据源 @State private var items = Array(0..<20) // 标记列表是否处于顶部 @State private var isListOnTop = true var body: some View { NavigationStack { List { // 顶部透明占位视图,用于检测滚动偏移 Color.clear .frame(height: 0) .background(GeometryReader { geo in Color.clear .preference( key: ScrollOffsetPreferenceKey.self, value: geo.frame(in: .named("listScroll")).minY ) }) ForEach(items, id: \.self) { item in Text("条目 \(item)") } } // 给列表滚动区域定义独立坐标系 .coordinateSpace(name: "listScroll") // 监听滚动偏移量变更,更新顶部状态 .onPreferenceChange(ScrollOffsetPreferenceKey.self) { offset in // 预留2的偏移误差范围,适配边界滚动的抖动问题 isListOnTop = offset <= 2 } // 仅在列表处于顶部时启用动画,监听数据源变化触发 .animation(isListOnTop ? .default : nil, value: items) .navigationTitle("列表示例") .toolbar { Button("新增条目") { // 新增元素到列表头部 items.insert(Int.random(in: 100..<200), at: 0) } } } } }
注意事项
- 阈值适配:如果页面包含导航栏、搜索栏、自定义顶部视图,需要调整
isListOnTop的判断阈值,可叠加当前页面的顶部安全区高度进行计算。 - 动画精度:
animation修饰符必须指定value参数为列表数据源,避免其他状态变更触发意外动画。 - 兼容场景:该方案同时适配下拉刷新后新增元素的动画判断需求。
内容的提问来源于stack exchange,提问作者KrmX
相关产品推荐
相关产品推荐

