SwiftUI点击列表Checkbox生成指定结构JSON数组的实现问题
问题核心原因
你当前的实现存在4个关键逻辑错误:
- 存储逻辑错误:
AddNurseryCard是列表的单个子项视图,你把全局的nurseryData数组存在单个子项中,每个子项的数组是独立的,根本无法累加所有选中项的数据 - 取值key错误:你定义的
data字典里数组对应的key是nurseryData,但取值时写的是data["nurseryCode"],永远拿不到正确的数组,只能得到空值 - 接口参数结构错误:你当前直接把单个选中项的参数传给接口,没有拼接成要求的
{"nurseryData": [选中项1, 选中项2...]}结构 - 缺少取消选中的处理逻辑:只处理了选中的情况,用户取消勾选时没有对应删除数组里的项
正确实现思路
- 把选中项的数组放在列表的父视图中统一管理,用
@State修饰保证状态变化时刷新视图 - 子项
AddNurseryCard通过回调闭包,把当前项的选中状态、对应ssoId和nurseryCode传递给父视图 - 父视图收到回调后,根据选中状态,往全局数组里添加/删除对应项,得到的数组天然就是你需要的结构
- 提交接口时直接把全局数组包装成要求的字典结构即可
修改后的代码示例
列表父视图示例
// 承载列表的父视图,你需要替换成你自己的列表页面代码 struct NurseryListView: View { // 全局存储所有选中的项,结构完全符合你要的nurseryData要求 @State private var selectedNurseryData: [[String: String]] = [] // 你的列表数据源,替换成你实际的数据源 let nurseryList: [AddNurseryModels.Data] = [] var body: some View { List(nurseryList, id: \.nurseryCode) { model in AddNurseryCard(models: model) { isSelected, ssoId, nurseryCode in if isSelected { // 选中时添加到数组 selectedNurseryData.append(["SSOId": ssoId, "nurseryCode": nurseryCode]) } else { // 取消选中时从数组删除对应项 selectedNurseryData.removeAll { $0["nurseryCode"] == nurseryCode } } // 如果需要每次选中就提交接口,在这里组装参数即可 let params: [String: Any] = [ "nurseryData": selectedNurseryData ] print(params) // 这里输出的就是你要求的JSON结构,直接传给接口即可 // addNursery(params: params) } } } // 接口请求方法放到父视图更合理 func addNursery(params :[String:Any]) { let url = Constants.init().devUrl + Api.init().addNursery let headers : HTTPHeaders = HTTPHeaders(["ApiKey" : Constants.init().apiKey]) AF.request(url,method: .post,parameters: params, encoding:JSONEncoding.default, headers: headers).responseJSON { response in switch response.result{ case .failure(let error): print(error) case .success(let json): print(json) } } } }
改造后的AddNurseryCard子项代码
import SwiftUI struct AddNurseryCard: View { @State private var checked = false let models : AddNurseryModels.Data // 选中状态变化的回调,传出是否选中、ssoId、苗圃编码 var onCheckChanged: (Bool, String, String) -> Void var body: some View { HStack{ Image("nursery") Divider() VStack(alignment: .leading) { if (models.isUrban ?? 0) != 0 { Text((models.nurseryName ?? "") + " (U)") } else { Text((models.nurseryName ?? "") + " (R)") } Text(models.rANGE_NAME ?? "") if (models.isActiveForGGAY ?? 0) != 0 { Text("Is Active For GGAY : Yes") } else { Text("Is Active For GGAY : NO") } } CheckBoxView(checked: $checked) .onAppear() { checked = (models.isActiveForGGAY ?? 0) != 0 } .onChange(of: checked, perform: { value in // 读取ssoId,建议这部分逻辑可以放到父视图,不用每个子项都重复读取 let result = UserDefaults.standard.value(forKey: "profile") let profile = ProfileModels(dict: result as? [String : Any] ?? [:]) let ssoId = (profile.data?.first?.ssoid ?? "") as String let nurseryCode = models.nurseryCode ?? "" // 把状态传给父视图处理 onCheckChanged(value, ssoId, nurseryCode) }) } Divider() } }
额外优化建议
- 可选值不要强行解包,用
??给默认值,避免闪退 UserDefaults读用户信息的逻辑建议只在父视图读一次,不用每个子Cell都重复读,性能更好- 如果你的列表数据量大,建议用结构体代替
[String: String]存储选中项,后续维护更方便
内容的提问来源于stack exchange,提问作者Rohit Sharma
相关产品推荐
相关产品推荐

