SwiftUI如何获取List当前显示单元格索引并实现滚动恢复
SwiftUI List 自动保存并恢复滚动位置(iOS16+)
核心实现思路
方案1:使用官方scrollPositionAPI(推荐)
iOS16及以上版本为List/ScrollView提供了scrollPosition(id:)修饰符,可直接绑定当前滚动聚焦的元素ID,无需手动监听滚动事件:
- 用
@State变量绑定滚动位置的索引 - 监听该变量的变化,自动将索引保存到
UserDefaults(或其他持久化存储) - 界面初始化时,从存储中读取保存的索引,赋值给绑定变量,List会自动滚动到对应位置
方案2:精确获取完全显示的单元格(自定义可见性检测)
如果需要精确追踪完全显示的最后一个单元格,可以结合GeometryReader监听每个单元格的可见区域,判断是否完全在屏幕内,再更新保存的索引:
- 为每个单元格添加
GeometryReader,获取其在滚动视图中的帧位置 - 判断帧是否完全处于屏幕可见范围内,若满足则更新保存的索引
- 恢复滚动位置时,通过
ScrollViewProxy的scrollTo方法定位到保存的索引
完整代码实现
方案1:官方API实现(简洁高效)
import SwiftUI private let lorem = "Lorem ipsum dolor sit amet, consectetur adipiscing elit." private let savedScrollKey = "LastListScrollIndex" struct ContentView: View { @State private var largeTextArray: [String] = (0..<500).map { _ in String(repeating: lorem, count: Int.random(in: 3..<10)) } @State private var scrollToIndex: Int? var body: some View { List { ForEach(largeTextArray.indices, id: \.self) { index in Text("\(index): ") .font(.title) .fontWeight(.heavy) + Text(largeTextArray[index]) } } // 绑定滚动位置索引 .scrollPosition(id: $scrollToIndex) .onAppear { // 从UserDefaults恢复保存的索引 scrollToIndex = UserDefaults.standard.integer(forKey: savedScrollKey) } .onChange(of: scrollToIndex) { _, newValue in // 滚动位置变化时自动保存 guard let newValue = newValue else { return } UserDefaults.standard.set(newValue, forKey: savedScrollKey) } } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }
方案2:精确检测完全显示的单元格
import SwiftUI private let lorem = "Lorem ipsum dolor sit amet, consectetur adipiscing elit." private let savedScrollKey = "LastVisibleListIndex" struct ContentView: View { @State private var largeTextArray: [String] = (0..<500).map { _ in String(repeating: lorem, count: Int.random(in: 3..<10)) } @State private var lastFullyVisibleIndex = 0 @State private var scrollProxy: ScrollViewProxy? var body: some View { ScrollViewReader { proxy in List { ForEach(largeTextArray.indices, id: \.self) { index in Text("\(index): ") .font(.title) .fontWeight(.heavy) + Text(largeTextArray[index]) .background( GeometryReader { geo in Color.clear .onAppear { scrollProxy = proxy } .onChange(of: geo.frame(in: .scrollView)) { frame in // 判断单元格是否完全处于屏幕可见区域 let screenHeight = UIScreen.main.bounds.height let isFullyVisible = frame.minY >= 0 && frame.maxY <= screenHeight // 仅更新为更大的索引(保证是最后一个完全显示的) if isFullyVisible && index > lastFullyVisibleIndex { lastFullyVisibleIndex = index UserDefaults.standard.set(index, forKey: savedScrollKey) } } } ) } } } .onAppear { // 恢复到保存的索引位置 let savedIndex = UserDefaults.standard.integer(forKey: savedScrollKey) scrollProxy?.scrollTo(savedIndex, anchor: .top) } } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }
内容的提问来源于stack exchange,提问作者Mac
相关产品推荐
相关产品推荐

