SwiftUI加载本地JSON列表后,无法滚动至指定索引问题求助
问题分析与解决方案
核心问题点
- Presenter未发布数据更新:
countries属性未用@Published修饰,导致View无法感知数据加载完成的变化,onChange监听失效。 - ScrollViewReader层级错误:原代码把
ScrollViewReader放在ScrollView内部,导致滚动代理无法正确控制滚动视图。 - 滚动目标标识不明确:用
CountryInfoValue本身作为id,滚动时无法直接通过索引定位,且存在Equatable判断风险。
修复步骤
1. 修改Presenter,让数据变化能通知View
class CountryListPresenter: ObservableObject { let navigationTitle: String let searchTitle: String @Published var countries = [CountryInfoValue]() // 添加@Published var selectedIndex = 50 init() { navigationTitle = "Select Country Code" searchTitle = "Search" fetchCountryList() } func fetchCountryList() { guard let url = Bundle.main.url(forResource: "countries.emoji", withExtension: "json") else { return } do { let data = try Data(contentsOf: url) let countryInfo = try JSONDecoder().decode(CountryInfo.self, from: data) var countryList = countryInfo.map { $0.value } countryList.sort(by: { $0.name < $1.name }) countries = countryList } catch { print("JSON加载/解析失败:\(error)") } } }
2. 调整View结构,实现正确滚动控制
struct SignUpEnterPhoneNumberView: View { @StateObject private var presenter = CountryListPresenter() @State private var searchText = "" var body: some View { VStack { NavigationStack { // 让ScrollViewReader包裹ScrollView ScrollViewReader { proxy in ScrollView { // 用索引作为ForEach的id,并给每个元素绑定索引标识 ForEach(presenter.countries.indices, id: \.self) { index in let country = presenter.countries[index] HStack(spacing: 10) { Text(country.emoji ?? "") Text(country.phone) Text(country.name) Spacer() } .id(index) } } .navigationBarTitle(Text("Select Country Code"), displayMode: .inline) // 监听数据加载完成事件 .onChange(of: presenter.countries) { _ in guard presenter.countries.count > presenter.selectedIndex else { return } proxy.scrollTo(presenter.selectedIndex, anchor: .top) } // 视图出现时如果数据已加载,也触发滚动 .onAppear { guard presenter.countries.count > presenter.selectedIndex else { return } proxy.scrollTo(presenter.selectedIndex, anchor: .top) } } Spacer() } .searchable(text: $searchText, prompt: presenter.searchTitle) } } }
关键说明
@Published修饰countries后,数据更新会自动通知View刷新,确保onChange能触发。- 调整
ScrollViewReader与ScrollView的层级,让代理能正确控制滚动视图的内容。 - 使用索引作为滚动目标的
id,避免模型本身作为标识可能带来的匹配问题,同时直接通过索引定位更直观。 - 增加越界判断,防止
selectedIndex超过列表长度导致崩溃。
内容的提问来源于stack exchange,提问作者mars_dev
相关产品推荐
相关产品推荐

