You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI加载本地JSON列表后,无法滚动至指定索引问题求助

问题分析与解决方案

核心问题点

  1. Presenter未发布数据更新:countries属性未用@Published修饰,导致View无法感知数据加载完成的变化,onChange监听失效。
  2. ScrollViewReader层级错误:原代码把ScrollViewReader放在ScrollView内部,导致滚动代理无法正确控制滚动视图。
  3. 滚动目标标识不明确:用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 22:52:46