如何在SwiftUI的ContentView中正确使用外部结构体替换示例数组?
解决ContentView中替换示例数组为自定义结构体数据的问题
步骤1:调整ContentView的状态变量
把原示例数组替换为存储国家名称的状态变量,保留查询链接:
struct ContentView: View { let countriesQuery = "https://query.wikidata.org/sparql?query=SELECT%20DISTINCT%20%3Fcountry%20%3FcountryLabel%0AWHERE%0A%7B%0A%20%20%3Fcountry%20wdt%3AP31%20wd%3AQ3624078.%0A%20%20SERVICE%20wikibase%3Alabel%20%7B%20bd%3AserviceParam%20wikibase%3Alanguage%20%22en%22%20%7D%0A%7D&format=json" @State private var countryLabels: [String] = [] // 存储提取后的国家名称数组 @State var selectedCountry = "" // ... }
步骤2:添加异步数据加载逻辑
用.task修饰符在视图初始化时异步加载数据,从自定义结构体中提取国家名称并更新状态变量:
var body: some View { VStack { Picker("", selection: $selectedCountry) { ForEach(countryLabels, id: \.self) { Text($0) } } } .task { // 异步获取并解码结构体数据 let wikiData: CountriesWikiData = await decodeWikiData(from: countriesQuery) // 从结构体中提取国家名称(取countryLabel的value字段) countryLabels = wikiData.results.bindings.compactMap { $0.countryLabel["value"] } // 可选:设置默认选中第一个国家 if let firstCountry = countryLabels.first { selectedCountry = firstCountry } } }
步骤3:优化解码函数(可选)
移除原函数中多余的JSON序列化步骤,直接用JSONDecoder解码,同时优化错误提示:
func decodeWikiData<T: Decodable>(from urlString: String) async -> T { guard let url = URL(string: urlString) else { fatalError("无效的查询URL") } let request = URLRequest(url: url) do { let (data, _) = try await URLSession.shared.data(for: request) return try JSONDecoder().decode(T.self, from: data) } catch { fatalError("数据加载或解码失败:\(error.localizedDescription)") } }
完整修改后的ContentView代码
struct ContentView: View { let countriesQuery = "https://query.wikidata.org/sparql?query=SELECT%20DISTINCT%20%3Fcountry%20%3FcountryLabel%0AWHERE%0A%7B%0A%20%20%3Fcountry%20wdt%3AP31%20wd%3AQ3624078.%0A%20%20SERVICE%20wikibase%3Alabel%20%7B%20bd%3AserviceParam%20wikibase%3Alanguage%20%22en%22%20%7D%0A%7D&format=json" @State private var countryLabels: [String] = [] @State var selectedCountry = "" var body: some View { VStack { Picker("", selection: $selectedCountry) { ForEach(countryLabels, id: \.self) { Text($0) } } } .task { let wikiData: CountriesWikiData = await decodeWikiData(from: countriesQuery) countryLabels = wikiData.results.bindings.compactMap { $0.countryLabel["value"] } if let first = countryLabels.first { selectedCountry = first } } } }
关键说明
- 视图属性中不能直接使用
await,必须在.task这类异步上下文里调用异步函数 - Picker需要字符串数组,因此要从
CountriesWikiData结构体中提取countryLabel的具体值 - 用
@State修饰countryLabels,确保数据更新时视图自动刷新
内容的提问来源于stack exchange,提问作者Sergei Kirasirov
相关产品推荐
相关产品推荐

