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

SwiftUI应用中如何在DetailsView中正确初始化ViewModel

问题原因

你的ViewModel自定义了要求传入urlString参数的构造器,但当前DetailsView中初始化ViewModel时未传入任何参数,同时你调用的buildIngredients方法也没有在ViewModel中实现,这两处是主要问题。

修复步骤

  1. 先给ViewModel补全缺失的buildIngredients方法,用于拼接配料列表:
class ViewModel: ObservableObject {
    
    let networkManager = NetworkManager()
    var urlString: String
    @Published var drinks: Drinks = Drinks(cocktails: [Cocktail]())
    @Published var dataIsFound: Bool = true
    
    init(urlString: String) {
        self.urlString = urlString
        fetchData() // 方法名小写开头符合Swift命名规范
    }
    
    func fetchData() {
        networkManager.fetchData(urlString) { results, error in
            DispatchQueue.main.async {
                self.drinks = results
                self.dataIsFound = !error
            }
        }
    }

    // 新增配料拼接方法
    func buildIngredients(_ cocktail: Cocktail) -> [String] {
        var ingredients = [String]()
        let mirror = Mirror(reflecting: cocktail)
        for child in mirror.children {
            if let key = child.label, key.hasPrefix("strIngredient"), let value = child.value as? String, !value.isEmpty {
                ingredients.append(value)
            }
        }
        return ingredients
    }
}
  1. 修改DetailsView,添加接收外部url的属性,初始化ViewModel时传入url:
struct DetailsView: View {
    // 新增属性,用于接收上层页面传入的查询接口地址
    let queryUrl: String
    // 声明ViewModel存储属性
    @StateObject private var viewModel: ViewModel
    
    // 自定义构造器完成ViewModel初始化
    init(queryUrl: String) {
        self.queryUrl = queryUrl
        // @StateObject需要通过下划线访问底层存储属性完成初始化
        _viewModel = StateObject(wrappedValue: ViewModel(urlString: queryUrl))
    }
        
    var body: some View {
        // 增加加载、空状态提示优化使用体验
        Group {
            if !viewModel.dataIsFound {
                Text("未找到匹配的鸡尾酒数据")
            } else if viewModel.drinks.cocktails.isEmpty {
                ProgressView("加载中...")
            } else {
                List(viewModel.drinks.cocktails) { cocktail in
                    VStack(alignment: .center) {
                        HStack(alignment: .center) {
                            Text(cocktail.strDrink + "  -")
                                .frame(alignment: .center)
                            Text(cocktail.strAlcoholic)
                                .frame(alignment: .center)
                        }
                        
                        WebImage(url: URL(string: cocktail.strDrinkThumb))
                            .resizable()
                            .scaledToFit()
                            .frame(maxWidth: .infinity)
                            .padding(.horizontal, 10)
                        
                        Text("~ Ingredients List ~\n").frame(alignment: .center)
                        
                        ForEach(viewModel.buildIngredients(cocktail), id: \.self) { ingredient in
                            Text(ingredient)
                        }
                        
                        Text("\n~ Recipe Instructions ~\n\n")
                        
                        Text(cocktail.strInstructions + "\n").fixedSize(horizontal: false, vertical: true)
                    }
                }
                // navigationTitle需要挂载在导航容器下的根视图上,放在内层Text无效
                .navigationTitle("Cocktail by first letter")
            }
        }
    }
}
  1. 上层跳转页面调用示例:
    如果要查询首字母为a的鸡尾酒,跳转时传入对应接口地址即可:
NavigationLink("查看首字母A的鸡尾酒") {
    DetailsView(queryUrl: "https://www.thecocktaildb.com/api/json/v1/1/search.php?f=a")
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 18:36:07