使用SwiftUI基于另一个选择器的选择触发选择器数据。

huangapple go评论61阅读模式
英文:

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)
    }
}

huangapple
  • 本文由 发表于 2023年6月6日 12:52:34
  • 转载请务必保留本文链接:https://go.coder-hub.com/76411530.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定