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

SwiftUI API请求后通过@State/@Binding更新状态刷新视图无需reloadData

解决方案

核心逻辑说明

你完全不需要调用reloadData(),也不需要额外引入@Binding即可实现需求:

  • SwiftUI 是声明式数据驱动框架,只要@State修饰的变量发生变更,绑定该变量的视图区域会自动触发刷新,无需手动调用任何重载方法
  • @Binding仅用于跨视图共享可变状态的场景,你当前所有状态(菜谱列表recipes、搜索词searchText)都在RecipesView内部管理,没有跨视图传值需求,不需要额外使用@Binding

你现有的代码其实已经实现了点击分类按钮刷新列表的逻辑:点击分类按钮调用queryBySpirit请求数据后更新@State修饰的recipes,列表会自动刷新,唯一缺少的是搜索框和搜索接口的对接逻辑。


优化后代码实现

struct RecipesView: View {
    @State var recipes = [Recipe]()
    @State var searchText = ""

    var body: some View {
        VStack {
            HStack {
                TextField("搜索...", text: $searchText)
                    .padding(7)
                    .padding(.horizontal, 25)
                    .background(Color(.systemGray6))
                    .cornerRadius(8)
                    .padding(.horizontal, 10)
                    // 新增:搜索框按下回车时触发搜索
                    .onSubmit(of: .text) {
                        queryByKeyword(keyword: searchText)
                    }
                    // 可选:如果需要实时搜索(输入字符就触发请求),添加下面这行
                    // .onChange(of: searchText) { newValue in
                    //     queryByKeyword(keyword: newValue)
                    // }
            }
            VStack{
                HStack {
                    Text("Categories")
                        .bold()
                        .multilineTextAlignment(.trailing)
                        .padding(10)
                    
                    Spacer()
                }
                ScrollView(.horizontal, showsIndicators: false) {
                    HStack(spacing: 10) {
                        ForEach(SPIRITS, id:\.self){ spirit in
                            Button("\(spirit)", action: {
                                queryBySpirit(spirit: spirit)
                            })
                            .frame(width: 80, height: 40, alignment: .center)
                            .background(Color("Tope"))
                            .foregroundColor(Color("Onyx"))
                            .cornerRadius(15)
                            .font(.subheadline.bold())
                        }
                    }.padding([.leading, .trailing])
                }
            }
            ScrollView {
                VStack {
                    ForEach(recipes) { recipe in
                        NavigationLink(destination: RecipeView(recipe: recipe)) {
                            RecipeCard(recipe: recipe)
                        }
                    }.padding([.trailing])
                }
            }
        }
        .onAppear(perform: loadData)
        .navigationBarTitle("Recipes")
        .listStyle(InsetGroupedListStyle())

        Color.gray.ignoresSafeArea()
    }

    func loadData() {
        AF.request("http://localhost:3000/recipes").responseJSON { response in
            guard let data = response.data else { return }
            if let response = try? JSONDecoder().decode([Recipe].self, from: data) {
                DispatchQueue.main.async {
                    self.recipes = response
                }
                return
            }
        }
    }

    func queryBySpirit(spirit: String) {
        // 优化:用Alamofire参数传递,避免特殊字符拼接URL出错
        AF.request("http://localhost:3000/recipes", parameters: ["spirit": spirit]).responseJSON { response in
            guard let data = response.data else { return }
            if let response = try? JSONDecoder().decode([Recipe].self, from: data) {
                DispatchQueue.main.async {
                    self.recipes = response
                }
                return
            }
        }
    }

    // 新增:关键词搜索接口请求方法
    func queryByKeyword(keyword: String) {
        // 这里的parameter key(比如keyword)根据你后端接口的实际要求修改
        AF.request("http://localhost:3000/recipes", parameters: ["keyword": keyword]).responseJSON { response in
            guard let data = response.data else { return }
            if let response = try? JSONDecoder().decode([Recipe].self, from: data) {
                DispatchQueue.main.async {
                    self.recipes = response
                }
                return
            }
        }
    }
}

效果说明

修改完成后,不管是点击分类按钮,还是在搜索框按下回车提交搜索,只要recipes数组更新,列表就会自动刷新,完全不需要手动调用reloadData()。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 22:15:01