SwiftUI ScrollView在LazyVStack嵌套LazyVGrid分页加载时自动滚动到底部问题
问题根因
- 两层懒加载容器(LazyVStack嵌套LazyVGrid)同时工作时,SwiftUI布局系统无法正确计算内容高度变化后的滚动偏移量,会默认将滚动位置对齐到内容底部
- 缺少加载状态判断,ProgressView可见就会触发加载逻辑,自动滚到底部后ProgressView持续可见,形成无限加载死循环
- iOS 15及以上版本的ScrollView默认会在内容更新时尝试保持当前可见视图的位置,底部ProgressView属于动态插入的视图,会被系统判定为需要保持可见,导致滚动偏移被强制拉到底部
修复方案
方案1:新增加载状态判断,避免重复触发加载
在你的collection模型中新增isLoading布尔属性,网络请求发起时设为true,请求完成(无论成功失败)设为false,修改ProgressView的触发逻辑:
if self.collection.hasMore && !self.collection.isLoading { HStack { Spacer() ProgressView() Spacer() } .padding(.top, 40) .onAppear { self.collection.load(next: 20) } }
该修改可以快速解决无限加载的问题,即使滚动位置被拉到底部,也不会重复发起请求。
方案2:移除冗余的LazyVStack嵌套,避免布局冲突
LazyVGrid本身已经具备懒加载能力,不需要额外嵌套LazyVStack,直接将所有内容放到ScrollView下即可,布局逻辑会大幅简化:
ScrollView { LazyVGrid(columns: self.columns, spacing: 20.0) { if let products = self.collection.products, products.count > 0 { ForEach(products) { inProduct in NavigationLink(destination: ProductDetailsContainerView(searchResult: WNProductSearchResult(product: inProduct))) { ProductListingCell(imageURL: inProduct.imageURL, title: inProduct.name, listingCount: inProduct.availableCount, price: inProduct.marketPrice) } } } } .padding(20) if let products = self.collection.products, products.count == 0 { self.emptyDisplay .padding(20) } if self.collection.hasMore && !self.collection.isLoading { HStack { Spacer() ProgressView() Spacer() } .padding(.top, 40) .onAppear { self.collection.load(next: 20) } } }
方案3:固定ScrollView滚动行为(iOS 16+支持)
如果需要保留LazyVStack嵌套结构,可以给ScrollView添加明确的滚动行为修饰符,禁止系统自动调整滚动偏移:
ScrollView { // 原有内容不变 } .scrollTargetBehavior(.custom) .scrollBounceBehavior(.basedOnSize) .automaticallyAdjustsScrollIndicatorInsets(false)
内容的提问来源于stack exchange,提问作者Rick
相关产品推荐
相关产品推荐

