首先,我對菜鳥的問題感到非常抱歉,但我似乎無法弄清楚這一點。
我對編碼非常陌生,剛剛開始接觸 SwiftUI,學習了一些課程并開始嘗試創建一些基本的應用程式。
我目前正在開發一個執行 API 呼叫并顯示資料的應用程式。
我的問題是,我試圖將解碼后的資料放入一個陣列中,這聽起來很簡單,而且我認為我遺漏了一些很容易的東西,但對于我的生活,我似乎無法弄清楚。
以下是我擁有的可編碼結構
struct Drinks: Codable, Identifiable {
let id = UUID()
let strDrink : String
let strInstructions: String
let strDrinkThumb: String?
let strIngredient1: String?
let strIngredient2: String?
let strIngredient3: String?
let strIngredient4: String?
let strIngredient5: String?
}
我想將成分放入一個陣列中,以便我可以在串列等中瀏覽它們
import SwiftUI
struct IngredientView: View {
let drink : Drinks
let ingredientArray : [String] = [] // I want to append the ingredients here
var body: some View {
GroupBox() {
DisclosureGroup("Drink Ingredience") {
ForEach(0..<3) { item in
Divider().padding(.vertical, 2)
HStack {
Group {
// To use the array here
}
.font(Font.system(.body).bold())
Spacer(minLength: 25)
}
}
}
}
}
}
再次,對于可能有一個簡單答案的菜鳥問題感到抱歉,但值得一試:D
謝謝!
uj5u.com熱心網友回復:
您可以使用這種方法將所有成分放入一個陣列中并在串列中使用它。這個想法是使用一個函式將所有成分收集到一個Ingredient物件陣列中
。您還可以使用計算屬性。最好使用一個Ingredient物件并宣告它,Identifiable
這樣當你在 list 和 ForEach 中使用它們時,每個物件都是
唯一的,即使名稱相同。
import SwiftUI
@main
struct TestApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
struct ContentView: View {
@State var drinkList = [Drink]()
var body: some View {
List {
ForEach(drinkList) { drink in
VStack {
Text(drink.strDrink).foregroundColor(.blue)
Text(drink.strInstructions)
ForEach(drink.allIngredients()) { ingr in
HStack {
Text(ingr.name).foregroundColor(.red)
Text(ingr.amount).foregroundColor(.black)
}
}
}
}
}
.task {
let theResponse: ApiResponse? = await getData(from: "https://www.thecocktaildb.com/api/json/v1/1/search.php?s=margarita")
if let response = theResponse {
drinkList = response.drinks
}
}
}
func getData<T: Decodable>(from urlString: String) async -> T? {
guard let url = URL(string: urlString) else {
print(URLError(.badURL))
return nil // <-- todo, deal with errors
}
do {
let (data, response) = try await URLSession.shared.data(for: URLRequest(url: url))
guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
print(URLError(.badServerResponse))
return nil // <-- todo, deal with errors
}
return try JSONDecoder().decode(T.self, from: data)
}
catch {
print("---> error: \(error)")
return nil // <-- todo, deal with errors
}
}
}
struct ApiResponse: Decodable {
var drinks: [Drink]
}
struct Drink: Decodable, Identifiable {
let id = UUID()
let idDrink: String
let strDrink: String
let strDrinkThumb: String
let strAlcoholic: String
let strGlass: String
let strInstructions: String
let strIngredient1: String?
let strIngredient2: String?
let strIngredient3: String?
let strIngredient4: String?
let strIngredient5: String?
let strIngredient6: String?
let strIngredient7: String?
let strIngredient8: String?
let strIngredient9: String?
let strIngredient10: String?
var strMeasure1: String?
var strMeasure2: String?
var strMeasure3: String?
var strMeasure4: String?
var strMeasure5: String?
var strMeasure6: String?
var strMeasure7: String?
var strMeasure8: String?
var strMeasure9: String?
var strMeasure10: String?
// --- here adjust to your needs, could also use a computed property
func allIngredients() -> [Ingredient] {
return [
Ingredient(name: strIngredient1 ?? "", amount: strMeasure1 ?? ""),
Ingredient(name: strIngredient2 ?? "", amount: strMeasure2 ?? ""),
Ingredient(name: strIngredient3 ?? "", amount: strMeasure3 ?? ""),
Ingredient(name: strIngredient4 ?? "", amount: strMeasure4 ?? ""),
Ingredient(name: strIngredient5 ?? "", amount: strMeasure5 ?? ""),
Ingredient(name: strIngredient6 ?? "", amount: strMeasure6 ?? ""),
Ingredient(name: strIngredient7 ?? "", amount: strMeasure7 ?? ""),
Ingredient(name: strIngredient8 ?? "", amount: strMeasure8 ?? ""),
Ingredient(name: strIngredient9 ?? "", amount: strMeasure9 ?? ""),
Ingredient(name: strIngredient10 ?? "", amount: strMeasure10 ?? "")
].filter{!$0.name.isEmpty}
}
}
struct Ingredient: Identifiable {
let id = UUID()
var name: String
var amount: String
}
uj5u.com熱心網友回復:
將您的結構更改為
struct Drink: Codable, Identifiable {
let id = UUID()
let strDrink : String
let strInstructions: String
let strDrinkThumb: String?
let strIngredients: [String] = []
}
無論您想在何處遍歷成分,都可以使用drink.strIngredients陣列
uj5u.com熱心網友回復:
首先,您的設計無法作業,因為您忽略了根物件,一個帶鍵的結構 drinks
struct Root : Decodable {
let drinks : [Drink]
}
一種可能的解決方案是撰寫自定義init方法。但是這有點棘手,因為您必須注入動態CodingKeys才能解碼回圈中的成分
首先創建一個自定義CodingKey結構
struct AnyKey: CodingKey {
var stringValue: String
var intValue: Int?
init?(stringValue: String) { self.stringValue = stringValue }
init?(intValue: Int) { return nil } // will never be called
}
在Drink結構體中——順便說一下,它應該以單數形式命名——指定第二個容器,動態創建成分鍵,在回圈中解碼成分并將結果附加到陣列中,直到值為nil。有人應該告訴服務的所有者,他們的 JSON 結構非常業余。由于您必須指定CodingKeys無論如何我將鍵映射到更有意義和更少冗余的結構成員名稱。
struct Drink: Decodable, Identifiable {
private enum CodingKeys : String, CodingKey {
case name = "strDrink", instructions = "strInstructions", thumbnail = "strDrinkThumb"
}
let id = UUID()
let name: String
let instructions: String
let thumbnail: URL
let ingredients: [String]
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.name = try container.decode(String.self, forKey: .name)
self.instructions = try container.decode(String.self, forKey: .instructions)
self.thumbnail = try container.decode(URL.self, forKey: .thumbnail)
var counter = 1
var temp = [String]()
let anyContainer = try decoder.container(keyedBy: AnyKey.self)
while true {
let ingredientKey = AnyKey(stringValue: "strIngredient\(counter)")!
guard let ingredient = try anyContainer.decodeIfPresent(String.self, forKey: ingredientKey) else { break }
temp.append(ingredient)
counter = 1
}
ingredients = temp
}
}
在視圖中顯示這樣的成分
struct IngredientView: View {
let drink : Drink
var body: some View {
GroupBox() {
DisclosureGroup("Drink Ingredience") {
ForEach(drink.ingredients) { ingredient in
Divider().padding(.vertical, 2)
HStack {
Group {
Text(ingredient)
}
.font(Font.system(.body).bold())
Spacer(minLength: 25)
}
}
}
}
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/381566.html
