SwiftUI中使用.searchable从数组动态展示关联数据的问题
SwiftUI .searchable组件搜索关联数据及列表重复问题解决方案
核心问题分析
- 数据结构设计不合理:三个独立枚举+全局数组的方式,无法保证名称、编码、价格的对应关系,且全局数组在视图刷新时会被反复填充,导致数据重复。
- 视图生命周期错误:在
body中直接调用createOtherWound()和removeAll(),而SwiftUI的body会频繁重绘,这会导致数据被反复清空和添加,引发列表重复。 - 搜索结果仅返回名称字符串,无法关联对应的编码和价格数据。
解决方案步骤
1. 重构数据模型,整合关联数据
把分散的名称、编码、价格整合到一个结构体中,确保三者一一对应,避免索引匹配错误:
// 整合后的伤口数据模型 struct WoundItem: Identifiable { let id = UUID() let name: String let code: String let price: Double } // 基于原有枚举生成完整数据数组,保证对应关系 extension WoundItem { static let allItems: [WoundItem] = [ WoundItem(name: OtherWoundsName.secondaryclosure_name.rawValue, code: OtherWoundsCode.secondaryclosure_code.rawValue, price: OtherWoundsValue.secondaryclosure_value.rawValue), WoundItem(name: OtherWoundsName.musclerepair_name.rawValue, code: OtherWoundsCode.musclerepair_code.rawValue, price: OtherWoundsValue.musclerepair_value.rawValue), WoundItem(name: OtherWoundsName.woundone_name.rawValue, code: OtherWoundsCode.woundone_code.rawValue, price: OtherWoundsValue.woundone_value.rawValue), WoundItem(name: OtherWoundsName.woundtwo_name.rawValue, code: OtherWoundsCode.woundtwo_code.rawValue, price: OtherWoundsValue.woundtwo_value.rawValue), WoundItem(name: OtherWoundsName.woundthree_name.rawValue, code: OtherWoundsCode.woundthree_code.rawValue, price: OtherWoundsValue.woundthree_value.rawValue), WoundItem(name: OtherWoundsName.woundfourmore_name.rawValue, code: OtherWoundsCode.woundfourmore_code.rawValue, price: OtherWoundsValue.woundfourmore_value.rawValue), WoundItem(name: OtherWoundsName.debrideone_name.rawValue, code: OtherWoundsCode.debrideone_code.rawValue, price: OtherWoundsValue.debrideone_value.rawValue), WoundItem(name: OtherWoundsName.debridetwo_name.rawValue, code: OtherWoundsCode.debridetwo_code.rawValue, price: OtherWoundsValue.debridetwo_value.rawValue), WoundItem(name: OtherWoundsName.extensortendon_name.rawValue, code: OtherWoundsCode.extensortendon_code.rawValue, price: OtherWoundsValue.extensortendon_value.rawValue), WoundItem(name: OtherWoundsName.flexortendor_name.rawValue, code: OtherWoundsCode.flexortendor_code.rawValue, price: OtherWoundsValue.flexortendor_value.rawValue), WoundItem(name: OtherWoundsName.fbskin_name.rawValue, code: OtherWoundsCode.fbskin_code.rawValue, price: OtherWoundsValue.fbskin_value.rawValue), WoundItem(name: OtherWoundsName.fbskinsedation_name.rawValue, code: OtherWoundsCode.fbskinsedation_code.rawValue, price: OtherWoundsValue.fbskinsedation_value.rawValue), WoundItem(name: OtherWoundsName.fbdermissedation_name.rawValue, code: OtherWoundsCode.fbdermissedation_code.rawValue, price: OtherWoundsValue.fbdermissedation_value.rawValue), WoundItem(name: OtherWoundsName.flapface5_name.rawValue, code: OtherWoundsCode.flapface5_code.rawValue, price: OtherWoundsValue.flapface5_value.rawValue), WoundItem(name: OtherWoundsName.flapother5_name.rawValue, code: OtherWoundsCode.flapother5_code.rawValue, price: OtherWoundsValue.flapother5_value.rawValue), WoundItem(name: OtherWoundsName.flapface5plus_name.rawValue, code: OtherWoundsCode.flapface5plus_code.rawValue, price: OtherWoundsValue.flapface5plus_value.rawValue), WoundItem(name: OtherWoundsName.flapother5plus_name.rawValue, code: OtherWoundsCode.flapother5plus_code.rawValue, price: OtherWoundsValue.flapother5plus_value.rawValue), WoundItem(name: OtherWoundsName.fingerdebride_name.rawValue, code: OtherWoundsCode.fingerdebride_code.rawValue, price: OtherWoundsValue.fingerdebride_value.rawValue), WoundItem(name: OtherWoundsName.handdebride_name.rawValue, code: OtherWoundsCode.handdebride_code.rawValue, price: OtherWoundsValue.handdebride_value.rawValue), WoundItem(name: OtherWoundsName.facedebride_name.rawValue, code: OtherWoundsCode.facedebride_code.rawValue, price: OtherWoundsValue.facedebride_value.rawValue), WoundItem(name: OtherWoundsName.otherdebride_name.rawValue, code: OtherWoundsCode.otherdebride_code.rawValue, price: OtherWoundsValue.otherdebride_value.rawValue), WoundItem(name: OtherWoundsName.ampulatephalanx_name.rawValue, code: OtherWoundsCode.ampulatephalanx_code.rawValue, price: OtherWoundsValue.ampulatephalanx_value.rawValue), WoundItem(name: OtherWoundsName.revisionfingertip_name.rawValue, code: OtherWoundsCode.revisionfingertip_code.rawValue, price: OtherWoundsValue.revisionfingertip_value.rawValue) ] } // 保留原有枚举(如果不需要可删除,这里用于生成数据) enum OtherWoundsName: String, CaseIterable { case secondaryclosure_name = "Secondary Closure" case musclerepair_name = "Muscle Repair (including skin)" case woundone_name = "Debride Wound/Ulcer - One" case woundtwo_name = "Debride Wound/Ulcer - Two" case woundthree_name = "Debride Wound/Ulcer - Three" case woundfourmore_name = "Debride Wound/Ulcer - Four Plus" case debrideone_name = "Debride into Tendon/Bone/Bursa/Bone" case debridetwo_name = "Debride into Tendon/Bone/Bursa/Bone - Two or More" case extensortendon_name = "Extensor Tendon Repair" case flexortendor_name = "Flexor Tendon Repair" case fbskin_name = "FB Skin" case fbskinsedation_name = "FB Skin with Sedation" case fbdermissedation_name = "FB Dermis with Sedation" case flapface5_name = "Advancement Flap: Neck Up < 5.1cm" case flapother5_name = "Advancement Flap: Other < 5.1cm" case flapface5plus_name = "Advancement Flap: Neck up > 10.1cm" case flapother5plus_name = "Advancement Flap: Other > 10.1.cm" case fingerdebride_name = "Finger Debridement" case handdebride_name = "Hand Debridement" case facedebride_name = "Face Debridement" case otherdebride_name = "Other per % BSA" case ampulatephalanx_name = "Amputate Phalanx" case revisionfingertip_name = "Revision of Amputation Finger Tip" } enum OtherWoundsCode: String, CaseIterable { case secondaryclosure_code = "Z738" case musclerepair_code = "R525" case woundone_code = "Z080" case woundtwo_code = "Z081" case woundthree_code = "Z082" case woundfourmore_code = "Z083" case debrideone_code = "Z084" case debridetwo_code = "Z085" case extensortendon_code = "R578" case flexortendor_code = "R585" case fbskin_code = "Z114" case fbskinsedation_code = "Z115" case fbdermissedation_code = "R517" case flapface5_code = "R011" case flapother5_code = "R002" case flapface5plus_code = "R012" case flapother5plus_code = "R003" case fingerdebride_code = "R660" case handdebride_code = "R661" case facedebride_code = "R662" case otherdebride_code = "R637" case ampulatephalanx_code = "R606" case revisionfingertip_code = "R629" } enum OtherWoundsValue: Double, CaseIterable { case secondaryclosure_value = 97.35 case musclerepair_value = 88.60 case woundone_value = 20 case woundtwo_value = 30 case woundthree_value = 45 case woundfourmore_value = 60 case debrideone_value = 60.001 case debridetwo_value = 90 case extensortendon_value = 164.10 case flexortendor_value = 307.60 case fbskin_value = 25.20 case fbskinsedation_value = 88.80 case fbdermissedation_value = 107.70 case flapface5_value = 89.85 case flapother5_value = 67.40 case flapface5plus_value = 247.15 case flapother5plus_value = 161.75 case fingerdebride_value = 28.90 case handdebride_value = 47.85 case facedebride_value = 28.901 case otherdebride_value = 29.68 case ampulatephalanx_value = 161.45 case revisionfingertip_value = 241.55 }
2. 重构ContentView,修复视图生命周期和搜索逻辑
移除全局数组,改用本地状态管理,直接使用预定义的静态数组,并基于整合后的模型实现搜索:
struct ContentView: View { @State private var searchText = "" // 直接使用预定义的完整伤口数据数组 private let allWounds = WoundItem.allItems var body: some View { NavigationView { List(searchResults) { item in VStack(alignment: .leading, spacing: 8) { Text(item.name) .font(.caption) HStack { Text("Code: \(item.code)") .font(.caption) Spacer() Text(String(format: "$%.2f", item.price)) .font(.caption) } } .padding() .border(Color.gray) } .searchable(text: $searchText, prompt: "Search wounds...") } } // 基于整合模型的搜索结果计算属性 var searchResults: [WoundItem] { if searchText.isEmpty { return [] // 搜索为空时不展示数据,需展示全部可替换为allWounds } else { return allWounds.filter { $0.name.localizedCaseInsensitiveContains(searchText) } } } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }
3. 关键优化点说明
- 数据结构整合:用
WoundItem结构体绑定名称、编码、价格,彻底解决三者关联问题,ForEach中可直接获取所有字段。 - 避免重复加载:移除全局数组和
createOtherWound()方法,改用静态预定义的allItems数组,不会因视图重绘反复填充数据。 - 搜索逻辑优化:搜索结果返回完整的
WoundItem对象,而非单独的名称字符串,直接支持展示所有关联数据。 - 视图生命周期修正:不在
body中执行数据修改操作,避免因SwiftUI自动重绘导致的数据异常。
可选优化
如果需要保留原有枚举的动态生成逻辑(比如枚举可能扩展),可以替换WoundItem.allItems的生成方式,通过枚举的case索引匹配:
static let allItems: [WoundItem] = zip(zip(OtherWoundsName.allCases, OtherWoundsCode.allCases), OtherWoundsValue.allCases).map { WoundItem(name: $0.0.0.rawValue, code: $0.0.1.rawValue, price: $0.1.rawValue) }
注意:这种方式要求三个枚举的case数量和顺序完全一致,否则会出现对应错误。
内容的提问来源于stack exchange,提问作者Amar Cheema
相关产品推荐
相关产品推荐

