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

SwiftUI List能否将.refreshable改为触底刷新而非顶部下拉刷新?

SwiftUI List 实现触底刷新

首先明确:SwiftUI 没有内置方法能直接把 .refreshable 的触发逻辑从顶部下拉改成底部上拉。.refreshable 本身就是专为下拉刷新设计的,要实现触底加载,得自己手动监听滚动状态来做。

下面给两种实用的实现方案:

方案一:监听UIScrollView滚动通知

利用SwiftUI List底层是UIScrollView的特性,通过监听滚动通知判断是否到达底部:

struct ContentView: View {
    @State private var items = Array(1..<100)
    @State private var isLoading = false // 防止重复触发加载
    
    var body: some View {
        NavigationStack {
            List {
                ForEach(items, id: \.self) { row in
                    Text("Row \(row)")
                }
                // 加载状态提示
                if isLoading {
                    ProgressView()
                        .frame(maxWidth: .infinity)
                }
            }
            .onReceive(NotificationCenter.default.publisher(for: UIScrollView.didScrollNotification)) { notification in
                guard let scrollView = notification.object as? UIScrollView else { return }
                
                let offsetY = scrollView.contentOffset.y
                let contentHeight = scrollView.contentSize.height
                let scrollViewHeight = scrollView.frame.size.height
                
                // 当滚动到距离底部20pt以内时触发加载
                if offsetY > contentHeight - scrollViewHeight - 20, !isLoading {
                    isLoading = true
                    // 模拟异步加载数据
                    DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
                        let newItems = Array((items.max() ?? 0)+1 ..< (items.max() ?? 0)+20)
                        items.append(contentsOf: newItems)
                        isLoading = false
                    }
                }
            }
        }
    }
}

方案二:利用列表最后元素的onAppear

在列表末尾添加一个透明占位行,当它出现在屏幕上时,说明已经滚动到底部:

struct ContentView: View {
    @State private var items = Array(1..<100)
    @State private var isLoading = false
    
    var body: some View {
        NavigationStack {
            List {
                ForEach(items, id: \.self) { row in
                    Text("Row \(row)")
                }
                // 加载状态提示
                if isLoading {
                    ProgressView()
                }
                // 透明占位行,用来触发触底逻辑
                Color.clear
                    .frame(height: 1)
                    .onAppear {
                        guard !isLoading else { return }
                        isLoading = true
                        // 模拟异步加载
                        DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
                            let newItems = Array((items.max() ?? 0)+1 ..< (items.max() ?? 0)+20)
                            items.append(contentsOf: newItems)
                            isLoading = false
                        }
                    }
            }
        }
    }
}

两种方案对比:

  • 滚动通知方案:判断更精确,能自定义触发的阈值(比如距离底部多少像素触发)
  • 占位行方案:代码更简洁,适合大多数场景

内容的提问来源于stack exchange,提问作者Andrew

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 19:12:47