SwiftUI List中ProgressView首次加载后不再显示的问题求助
分页列表ProgressView无法重复显示的问题修复
问题根源
你的实现存在两个核心问题:
- 第一个方案中,
.task修饰符仅在视图首次挂载时执行一次,后续即使滚动到底部,只要ProgressView没有被销毁重建,任务就不会再次触发。加上loadMore方法始终将hasMoreToLoad设为true,视图状态无变化,SwiftUI不会重新触发任务。 - 第二个方案用
isLoading切换视图,加载完成后ProgressView被移除,没有了触发加载的入口,自然无法再次显示。
修复方案
方案1:基于onAppear的基础实现
通过onAppear监听ProgressView的出现,结合isLoading防止重复加载,确保每次滚动到底部时都能触发新的加载任务。
struct SomeView : View { @ObservedObject var viewModel = ViewModel() var body : some View { List { Section { ForEach(0..<viewModel.items.count) { i in Text("\(viewModel.items[i])") .frame(maxWidth: .infinity, alignment: .center) } } // 仅当还有数据可加载且未在加载时显示ProgressView if viewModel.hasMoreToLoad && !viewModel.isLoading { Section { ProgressView() .frame(maxWidth: .infinity, alignment: .center) .onAppear { Task { await viewModel.loadMore() } } } } } } } class ViewModel : ObservableObject { @Published var items: [Int] = Array(0...10) @Published var hasMoreToLoad = true @Published var isLoading = false func loadMore() async { guard !isLoading else { return } // 阻止重复加载 isLoading = true defer { isLoading = false } // 确保加载完成后重置状态 // 模拟远程请求延迟 await Task.sleep(for: Duration.milliseconds(500)) // 添加新数据到列表 let newItems = Array(items.count..<items.count+10) items.append(contentsOf: newItems) // 模拟无更多数据的场景(比如加载3次后停止) if items.count >= 40 { hasMoreToLoad = false } } }
方案2:基于ScrollViewReader的精准监听
如果List的滚动触发不够灵敏,可使用ScrollViewReader主动监听最后一个列表项的可见状态,触发加载,这种方式更可靠。
struct SomeView : View { @ObservedObject var viewModel = ViewModel() var body : some View { List { Section { ForEach(0..<viewModel.items.count) { i in Text("\(viewModel.items[i])") .frame(maxWidth: .infinity, alignment: .center) .id(i) // 为每个列表项设置唯一ID,用于滚动监听 } } if viewModel.hasMoreToLoad { Section { ProgressView() .frame(maxWidth: .infinity, alignment: .center) .id("loading") // 为加载视图设置ID } } } .scrollViewReader { proxy in // 监听列表项数量变化,检查最后一项是否可见 .onChange(of: viewModel.items.count) { count in guard count > 0, viewModel.hasMoreToLoad, !viewModel.isLoading else { return } proxy.scrollTo(count - 1) { isVisible in if isVisible { Task { await viewModel.loadMore() } } } } // 可选:加载新数据后自动滚动到加载视图,提升体验 .onChange(of: viewModel.items) { _ in if viewModel.hasMoreToLoad { proxy.scrollTo("loading", anchor: .bottom) } } } } } class ViewModel : ObservableObject { @Published var items: [Int] = Array(0...10) @Published var hasMoreToLoad = true @Published var isLoading = false func loadMore() async { guard !isLoading else { return } isLoading = true defer { isLoading = false } await Task.sleep(for: Duration.milliseconds(500)) let newItems = Array(items.count..<items.count+10) items.append(contentsOf: newItems) if items.count >= 40 { hasMoreToLoad = false } } }
核心注意事项
- 一定要用
isLoading状态做防重校验,避免多次重复请求服务器。 - 当确认没有更多数据时,必须将
hasMoreToLoad设为false,否则ProgressView会一直显示。 .task修饰符适用于一次性任务,分页加载需要重复触发,onAppear或滚动监听是更合适的选择。
内容的提问来源于stack exchange,提问作者Brandon Stillitano
相关产品推荐
相关产品推荐

