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
相关产品推荐
相关产品推荐

