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

SwiftUI中基于Picker选择联动加载对应数据的问题求助

问题描述

我有两个API分别用于获取大陆和国家数据,希望实现选择大陆后自动更新对应国家列表的功能。目前用Picker分别调用API,但无法根据选中的大陆选项触发国家数据更新。我是Swift编程新手,术语若有不当请见谅,恳请帮忙解决。最终预期还能实现:选中国家后加载对应州/省数据选项。


原ContentView代码

import SwiftUI

struct ContentView: View {
    @StateObject private var ContinentView = ContinentListViewModel()
    @State private var isExpanded = false
    @State private var selected = "Asia"
    
    @StateObject private var CountryView = CountryViewModels()
    @State private var Expanding = false
    @State private var isselected = "india"
    var body: some View {
        VStack {
            Section{
                Form{
                    Picker("Country*", selection: $isselected) {
                        ForEach(CountryView.countries, id: \.id) {
                            countries in Text(countries.value).tag(countries.value)
                        }
                    }
                    .onAppear(perform: CountryView.fetchCountry)
                    Picker("Continent*", selection: $selected) {
                        ForEach(ContinentView.continents, id: \.id) {
                            continents in Text(continents.value).tag(continents.value)
                                .onTapGesture {
                                    self.selected = continents.value
                                    withAnimation {
                                        self.isExpanded.toggle()
                                    }
                                }
                        }
                    }
                    .onAppear(perform: ContinentView.loadData)
                }
            }
        }
        .padding()
    }
}

原CountryViewModel代码

import Foundation
import SwiftUI

struct CountryList: Hashable, Codable {
    let id: Int
    let value: String
}
class CountryViewModels: ObservableObject {
    
    @Published var countries: [CountryList] = []
    
    func fetchCountry() {
        
        guard let url = URL(string: "https://arvfsmmasterdatamicroservice.azurewebsites.net/api/MasterData/getCountriesByContinentId") else {
            return
        }
        
        
        let task = URLSession.shared.dataTask(with: url) { [weak self] data, _, error in
            guard let data = data, error == nil else {
                return
            }
            
            // convert to JSON
            do {
                let countries = try JSONDecoder().decode([CountryList].self, from: data)
                DispatchQueue.main.sync {
                    self?.countries = countries
                }
            }
            catch {
                print(error)
            }
        }
        task.resume()
    }
}

原ContinentViewModel代码

import Foundation
import SwiftUI

struct ContinentList: Hashable, Codable, Identifiable {
    let id: Int
    let value: String
}

class ContinentListViewModel: ObservableObject {
    @Published var continents: [ContinentList] = []
    @StateObject private var ContinentView = ContinentListViewModel() // 多余的声明
    func loadData() {
            guard let url = URL(string: "https://arvfsmmasterdatamicroservice.azurewebsites.net/api/MasterData/getContinents") else {
                return
            }
        
            let request = URLRequest(url: url)
            URLSession.shared.dataTask(with: request) { [weak self] data, continents, error in
                guard let data = data, error == nil else {
                    return
                }
                //convert to JSON
                do {
                    let continents = try JSONDecoder().decode([ContinentList].self, from: data)
                    DispatchQueue.main.async {
                        self?.continents = continents
                    }
                }
                catch {
                    print(error)
                }
            }.resume()
        }
}

解决方案

核心问题分析

  1. 国家API需要传入大陆ID才能返回对应国家列表,但原代码中fetchCountry方法未传递参数,导致API无法过滤数据。
  2. 未监听大陆选择的变化,无法触发国家数据的重新加载。
  3. ContinentViewModel中存在多余的@StateObject声明,会引发逻辑错误。

分步修复代码

1. 修复ContinentViewModel

移除内部多余的@StateObject声明:

import Foundation
import SwiftUI

struct ContinentList: Hashable, Codable, Identifiable {
    let id: Int
    let value: String
}

class ContinentListViewModel: ObservableObject {
    @Published var continents: [ContinentList] = []
    
    func loadData() {
        guard let url = URL(string: "https://arvfsmmasterdatamicroservice.azurewebsites.net/api/MasterData/getContinents") else {
            return
        }
    
        let request = URLRequest(url: url)
        URLSession.shared.dataTask(with: request) { [weak self] data, _, error in
            guard let data = data, error == nil else {
                return
            }
            do {
                let continents = try JSONDecoder().decode([ContinentList].self, from: data)
                DispatchQueue.main.async {
                    self?.continents = continents
                }
            } catch {
                print(error)
            }
        }.resume()
    }
}

2. 修改CountryViewModel,支持传入大陆ID

更新fetchCountry方法,接收大陆ID作为参数并拼接正确的API URL:

import Foundation
import SwiftUI

struct CountryList: Hashable, Codable, Identifiable { // 添加Identifiable,简化ForEach使用
    let id: Int
    let value: String
}

class CountryViewModels: ObservableObject {
    @Published var countries: [CountryList] = []
    
    // 修改方法,接收continentId参数
    func fetchCountry(by continentId: Int) {
        // 拼接带参数的API URL
        guard let url = URL(string: "https://arvfsmmasterdatamicroservice.azurewebsites.net/api/MasterData/getCountriesByContinentId?continentId=\(continentId)") else {
            return
        }
        
        let task = URLSession.shared.dataTask(with: url) { [weak self] data, _, error in
            guard let data = data, error == nil else {
                return
            }
            
            do {
                let countries = try JSONDecoder().decode([CountryList].self, from: data)
                DispatchQueue.main.async { // 改用async,避免主线程阻塞
                    self?.countries = countries
                }
            } catch {
                print(error)
            }
        }
        task.resume()
    }
}

3. 重构ContentView,实现联动逻辑

  • 监听大陆选择的变化,触发国家数据加载
  • 调整Picker的tag为大陆ID(而非名称),方便传递参数给API
  • 初始化时加载大陆数据,并自动加载默认大陆的国家列表:
import SwiftUI

struct ContentView: View {
    @StateObject private var continentVM = ContinentListViewModel()
    @StateObject private var countryVM = CountryViewModels()
    
    @State private var selectedContinentId: Int? = nil // 存储选中的大陆ID
    @State private var selectedCountry: String = ""
    
    var body: some View {
        VStack {
            Form {
                // 大陆选择Picker
                Picker("Continent*", selection: $selectedContinentId) {
                    Text("请选择大陆").tag(nil as Int?)
                    ForEach(continentVM.continents) { continent in
                        Text(continent.value).tag(continent.id as Int?)
                    }
                }
                .onAppear(perform: continentVM.loadData)
                // 监听大陆选择变化,加载对应国家
                .onChange(of: selectedContinentId) { newId in
                    guard let id = newId else {
                        countryVM.countries = []
                        selectedCountry = ""
                        return
                    }
                    countryVM.fetchCountry(by: id)
                    selectedCountry = "" // 切换大陆后清空国家选择
                }
                
                // 国家选择Picker(仅当有大陆选中时启用)
                Picker("Country*", selection: $selectedCountry) {
                    Text("请先选择大陆").tag("")
                    ForEach(countryVM.countries) { country in
                        Text(country.value).tag(country.value)
                    }
                }
                .disabled(selectedContinentId == nil)
            }
        }
        .padding()
    }
}

后续扩展州/省数据的思路

按照同样的逻辑扩展即可:

  1. 创建StateProvinceList模型和StateProvinceViewModel,实现根据国家ID获取州/省数据的方法
  2. 在ContentView中添加@StateObject private var stateVM = StateProvinceViewModel()和@State private var selectedCountryId: Int?
  3. 监听selectedCountry的变化,调用州/省API加载数据
  4. 添加州/省选择Picker,绑定selectedState

内容的提问来源于stack exchange,提问作者CodeBee

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 15:57:18