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
相关产品推荐
相关产品推荐

