SwiftUI中如何检测用户滚动至屏幕底部并触发函数
在SwiftUI中检测滚动至屏幕底部并触发函数的最佳方案
你当前的代码问题在于GeometryReader的位置不对——它被放在了ScrollView外面,获取的是父视图的frame,无法正确捕捉滚动内容的底部位置。下面是几种可靠的实现方式:
方法一:在滚动内容末尾添加检测视图(推荐适配LazyVGrid)
在你的LazyVGrid后面添加一个不可见的占位视图,用GeometryReader检测它是否进入可视区域,以此判断是否滚动到了底部:
var body: some View { NavigationView { VStack { ZStack { Text("Pokedex") .font(.custom("Pokemon Solid", size: 25)) .foregroundColor(.yellow) .opacity(0.3) ScrollView { LazyVGrid(columns: adaptiveColumns, spacing: 5) { ForEach(viewModel.filteredPokemon) { pokemon in NavigationLink(destination: PokemonDetailView(pokemon: pokemon)) { PokemonView(vm: viewModel, pokemon: pokemon) } } } .animation(.easeInOut(duration: 0.3), value: viewModel.filteredPokemon.count) // 底部检测视图 Color.clear .frame(height: 1) .background( GeometryReader { proxy in Color.clear .onAppear { let bottomViewTopEdge = proxy.frame(in: .global).minY let screenHeight = UIScreen.main.bounds.height // 当底部视图顶部进入屏幕时,触发加载 if bottomViewTopEdge < screenHeight { // 加加载状态防止重复触发 if !viewModel.isLoading { viewModel.loadMorePokemon() // 你的加载函数 } } } } ) } .navigationTitle("Pokedexarino") .navigationBarTitleDisplayMode(.inline) .searchable(text: $viewModel.searchText) } } } .environmentObject(viewModel) }
方法二:监听UIScrollView滚动通知
利用SwiftUI底层的UIScrollView通知,直接判断滚动偏移量:
var body: some View { NavigationView { VStack { ZStack { Text("Pokedex") .font(.custom("Pokemon Solid", size: 25)) .foregroundColor(.yellow) .opacity(0.3) ScrollView { LazyVGrid(columns: adaptiveColumns, spacing: 5) { ForEach(viewModel.filteredPokemon) { pokemon in NavigationLink(destination: PokemonDetailView(pokemon: pokemon)) { PokemonView(vm: viewModel, pokemon: pokemon) } } } .animation(.easeInOut(duration: 0.3), value: viewModel.filteredPokemon.count) } .navigationTitle("Pokedexarino") .navigationBarTitleDisplayMode(.inline) .searchable(text: $viewModel.searchText) // 监听滚动通知 .onReceive(NotificationCenter.default.publisher(for: UIScrollView.didScrollNotification)) { notification in guard let scrollView = notification.object as? UIScrollView else { return } let offsetY = scrollView.contentOffset.y let contentTotalHeight = scrollView.contentSize.height let scrollViewFrameHeight = scrollView.frame.size.height // 提前100px触发加载(可调整阈值) let triggerThreshold: CGFloat = 100 if offsetY > contentTotalHeight - scrollViewFrameHeight - triggerThreshold { if !viewModel.isLoading { viewModel.loadMorePokemon() } } } } } } .environmentObject(viewModel) }
关键注意点
- 必须添加加载状态标记(比如
isLoading),避免滚动时重复触发加载函数 - 方法一更适配LazyVGrid这类懒加载容器,因为它依赖内容的可见性;方法二则适合所有ScrollView场景
- 可以调整触发阈值(比如提前加载),让用户在滚动到底部前就开始加载内容,提升体验
内容的提问来源于stack exchange,提问作者Fredy Lopez
相关产品推荐
相关产品推荐

