如何获取分页ScrollView内LazyVStack滚动结束时的当前显示项
解决分页ScrollView滚动结束后获取当前显示项的问题
直接用scrollPosition结合onChange替代onAppear,就能解决重复触发和取消滚动不触发的问题,具体实现如下:
实现代码
struct SwippableView: View { var data: [VerseModel] @State private var currentVerseId: VerseModel.ID? // 跟踪当前滚动到的条目ID var body: some View { ScrollView(.vertical) { LazyVStack(spacing: 0) { ForEach(data, id: \.id) { verse in VerseView(verse: verse) .padding(0) .frame(maxWidth: .infinity, maxHeight: .infinity) } } .scrollTargetLayout() } .ignoresSafeArea() .scrollTargetBehavior(.paging) .scrollIndicators(.never) .scrollPosition(id: $currentVerseId) // 绑定滚动位置到当前条目的ID .onChange(of: currentVerseId) { _, newId in guard let newId = newId, let currentVerse = data.first(where: { $0.id == newId }) else { return } print("滚动结束,当前显示的项:\(currentVerse)") } } }
方案优势
- 仅在滚动完全结束后触发,不会像
onAppear那样重复调用 - 即使中途取消滚动(拖拽后松手回弹),也会自动更新到最终停留的条目,避免遗漏触发
- 分页模式下,
scrollPosition会精准匹配当前完全显示的全屏条目
可选:跟踪索引而非ID
如果更倾向于用索引跟踪,可修改代码如下:
struct SwippableView: View { var data: [VerseModel] @State private var currentIndex: Int? // 跟踪当前滚动到的条目索引 var body: some View { ScrollView(.vertical) { LazyVStack(spacing: 0) { ForEach(Array(data.enumerated()), id: \.element.id) { index, verse in VerseView(verse: verse) .padding(0) .frame(maxWidth: .infinity, maxHeight: .infinity) } } .scrollTargetLayout() } .ignoresSafeArea() .scrollTargetBehavior(.paging) .scrollIndicators(.never) .scrollPosition(id: $currentIndex) // 绑定滚动位置到当前条目的索引 .onChange(of: currentIndex) { _, newIndex in guard let newIndex = newIndex, newIndex < data.count else { return } let currentVerse = data[newIndex] print("滚动结束,当前显示的项:\(currentVerse)") } } }
内容的提问来源于stack exchange,提问作者Jaseel.Dev
相关产品推荐
相关产品推荐

