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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 15:11:29