英文:
triggering picker data based another picker selection using swiftui
问题
我有两个用于国家和大洲数据的API。我想根据所选的大洲更改国家数据。我尝试使用选择器从API分别获取数据,但无法根据所选的大洲选项操作国家数据。请帮助我解决这个问题。我是Swift编程的初学者,请原谅我的术语。谢谢。
我希望根据所选的国家来获取州数据选项。
这是视图的主体:
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()
}
}
我正在尝试将大洲和国家API连接到选择器,因此当选择大洲选择器时,它会触发国家API并相应地显示列表。
英文:
I have two API's for country and continent data. I want to change the country data based on the continent selection. I have tried to use the picker to fetch the data respectively from API, but i'm not able to manipulate the country data as per the selected continent option. Please help me with this issue. I'm beginner in the swift coding, please excuse my terminology. Thank you.
I'm expecting to get State data options based on the Country selected.
This the body of the view:
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()
}
}
I'm trying to connect the continent and country API to the picker, so when continent picker is selected it triggers the country API and view the list accordingly.
答案1
得分: 0
你的问题的核心在于获取国家的URL需要使用ContinentId。
let url = "https://arvfsmmasterdatamicroservice.azurewebsites.net/api/MasterData/getCountriesByContinentId?continentId=\(continentId)"
要做到这一点,你需要修改代码以使其更具代表性并连接到不同的步骤。
@State private var selectedContinent: ContinentModel? = nil
@State private var selectedCountry: CountryModel? = nil
这将使所选大陆/国家的所有数据可用。
然后当所选大陆发生变化时,获取新的国家列表。
.task(id: selectedContinent, {
selectedCountry = nil
countryVM.countries = []
//由于选择了新大陆,将选择的国家设为nil并清空列表
guard let selectedContinent else {
return
}
//获取新的国家列表
do {
try await countryVM.fetchCountry(continentId: selectedContinent.id)
} catch {
//应显示error.localizedDescription给用户。
print(error)
}
})
英文:
The core of your issue is that the URL for to retrieve the Countries needs the ContinentId
let url = "https://arvfsmmasterdatamicroservice.azurewebsites.net/api/MasterData/getCountriesByContinentId?continentId=\(continentId)"
To get to that you need to modify your code to be more representative and to connect to the different steps.
@State private var selectedContinent: ContinentModel? = nil
@State private var selectedCountry: CountryModel? = nil
This will make available all the data for the selected Continent/Country.
Then when the selected Continent changes get the new Country List.
.task(id: selectedContinent, {
selectedCountry = nil
countryVM.countries = []
//Since a new continent was selected makes the selected country nil and empty the list
guard let selectedContinent else { //Proceed if the continent isn't nil
return
}
//Get the new countries list
do {
try await countryVM.fetchCountry(continentId: selectedContinent.id)
} catch {
//should display error.localizedDescription to user.
print(error)
}
})
Here is the full code, I made some other changes to account for errors and misrepresentations.
import SwiftUI
struct CountryPicker: View {
@StateObject private var continentVM = ContinentListViewModel() //Variables should start with lowercase
@State private var selectedContinent: ContinentModel? = nil
@StateObject private var countryVM = CountryViewModels() //Variables should start with lowercase
@State private var selectedCountry: CountryModel? = nil
var body: some View {
VStack {
Section{
Form{
Picker("Continent*", selection: $selectedContinent) {
Text("Select a Continent").tag(nil as ContinentModel?)
ForEach(continentVM.continents, id: \.id) {
continent in Text(continent.value).tag(continent as ContinentModel?)
}
}
.task {
do {
try await continentVM.loadData()
} catch {
//should display error.localizedDescription to user.
print(error)
}
}
Picker("Country*", selection: $selectedCountry) {
Text("Select a Country").tag(nil as CountryModel?)
ForEach(countryVM.countries, id: \.id) {
country in
Text(country.value).tag(country as CountryModel?)
}
}
.task(id: selectedContinent, { //This will run when the selected continent changes
selectedCountry = nil
countryVM.countries = []
//Since a new continent was selected makes the selected country nil and empty the list
guard let selectedContinent else { //Proceed if the continent isn't nil
return
}
//Get the new countries list
do {
try await countryVM.fetchCountry(continentId: selectedContinent.id)
} catch {
//should display error.localizedDescription to user.
print(error)
}
})
}
}
}
.padding()
}
}
struct CountryModel: Hashable, Codable { //Rename to something that represents the object, this isnt a list it is a single object.
let id: Int
let value: String
}
class CountryViewModels: ObservableObject {
@Published var countries: [CountryModel] = []
func fetchCountry(continentId: Int) async throws {
let url = "https://arvfsmmasterdatamicroservice.azurewebsites.net/api/MasterData/getCountriesByContinentId?continentId=\(continentId)"
self.countries = try await URLSession.shared.decoded(url: url)
}
}
struct ContinentModel: Hashable, Codable, Identifiable { //Rename to something that represents the object, this isnt a list it is a single object.
let id: Int
let value: String
}
class ContinentListViewModel: ObservableObject {
@Published var continents: [ContinentModel] = []
@StateObject private var ContinentView = ContinentListViewModel()
func loadData() async throws {
let url = "https://arvfsmmasterdatamicroservice.azurewebsites.net/api/MasterData/getContinents"
self.continents = try await URLSession.shared.decoded(url: url)
}
}
extension URLSession {
func decoded<D>(url: String) async throws -> D where D: Decodable {
guard let url = URL(string: url) else {
throw URLError(.badURL)
}
let (data, response) = try await self.data(from: url)
guard let httpsReponse = response as? HTTPURLResponse else {
throw URLError(.badServerResponse)
}
guard httpsReponse.statusCode == 200 else { //Check the response, this accounts for network errors such as no web connection, web errors such as website unavailable, etc.
throw URLError(URLError.Code(rawValue: httpsReponse.statusCode))
}
return try JSONDecoder().decode(D.self, from: data)
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论