SwiftUI ScrollView触底检测异常,求修复方案
SwiftUI ScrollView触底加载优化方案
原代码的问题在于给每个列表项都添加了GeometryReader监听位置变化,滚动时所有可见项的frame更新都会触发onChange,再加上判断条件newFrame.minY < scrollViewHeight过于宽泛,导致启动和滑动时大量触发打印。
优化方案
- 只在ScrollView底部添加一个专用的触底检测视图,避免每个item都监听
- 使用更精确的触底判断逻辑:检测底部视图是否进入ScrollView的可见范围
- 添加加载状态控制,避免同一时间多次触发加载
修改后的完整代码
import SwiftUI struct FeedView: View { @StateObject private var viewModel = FeedViewModel() @State private var isLoading = false @Namespace private var scrollViewNamespace var body: some View { mainInterfaceView } } extension FeedView { var mainInterfaceView: some View { ScrollView { LazyVStack { ForEach(viewModel.tweets) { tweet in Button(action: {}) { someView() } } // 底部触底检测视图 Color.clear .frame(height: 1) .background( GeometryReader { proxy in Color.clear .onAppear { checkIfReachedBottom(proxy: proxy) } .onChange(of: proxy.frame(in: .named(scrollViewNamespace))) { _ in checkIfReachedBottom(proxy: proxy) } } ) } } .coordinateSpace(name: scrollViewNamespace) } private func checkIfReachedBottom(proxy: GeometryProxy) { // 避免重复加载 guard !isLoading else { return } let scrollViewFrame = proxy.frame(in: .named(scrollViewNamespace)) // 判断检测视图是否进入屏幕可见区域(底部进入即视为触底) if scrollViewFrame.minY <= UIScreen.main.bounds.height { isLoading = true print("called") // 模拟加载数据,加载完成后重置isLoading DispatchQueue.main.asyncAfter(deadline: .now() + 1) { viewModel.loadMoreTweets() isLoading = false } } } // 替换成你的实际视图 private func someView() -> some View { Text("Tweet Content") .padding() .frame(maxWidth: .infinity) .background(Color.gray.opacity(0.1)) .cornerRadius(8) } } // 示例ViewModel class FeedViewModel: ObservableObject { @Published var tweets: [Tweet] = (0..<20).map { Tweet(id: $0) } func loadMoreTweets() { let newTweets = (tweets.count..<tweets.count+10).map { Tweet(id: $0) } tweets.append(contentsOf: newTweets) } } // 示例Tweet模型 struct Tweet: Identifiable { let id: Int }
关键说明
- 底部的透明视图仅作为触底检测器,只有它的位置变化会触发监听,大幅减少不必要的回调
isLoading状态变量防止同一时间多次触发加载,避免重复打印"called"- 判断逻辑
scrollViewFrame.minY <= UIScreen.main.bounds.height确保只有当底部视图进入屏幕可见范围时才触发,你也可以根据需求调整阈值(比如提前100px触发加载)
内容的提问来源于stack exchange,提问作者user21041088
相关产品推荐
相关产品推荐

