Xcode iOS开发:通过UISwitch指定账单数据存入对应分类记录

核心问题
你现有代码的逻辑错误直接导致分类存储失效:
- 保存按钮的触发逻辑里,没有判断开关状态就把同一条账单追加到了所有分类的数组中,等于所有分类都会存全量数据
- 末尾的开关判断代码存在语法错误:
cars.append(newCar) = tblCar属于非法赋值,还重复向车辆账单数组追加了数据 - 数据模型设计冗余:6个分类的账单字段完全一致,却重复定义了6个结构体,后续维护成本极高
- 目前仅实现了车辆分类的开关输出口和表格绑定,其余分类的开关逻辑、数据展示逻辑完全缺失
- 所有输入值、图片对象都用强制解包,用户漏填内容、没拍照片时点击保存会直接触发崩溃
修正步骤
1. 重构统一数据模型
删除重复定义的Car/Went/South/Bruns/Home/Telephone结构体,替换为统一模型+分类枚举:
// 账单分类枚举,统一管理所有支出类别 enum BillCategory: String { case car, went, south, bruns, home, telephone } // 通用账单模型,所有分类共用 struct Bill { let provider: String let date: String let category: BillCategory }
替换原来的6个分类数组,用字典按分类存储账单,扩展性更强:
var billStorage: [BillCategory: [Bill]] = [ .car: [], .went: [], .south: [], .bruns: [], .home: [], .telephone: [] ] // 记录当前用户选中的账单分类,默认选中车辆分类 var currentSelectedCategory: BillCategory = .car
2. 实现开关互斥逻辑
把Storyboard里剩下5个分类的UISwitch都拖线成输出口,给所有开关绑定同一个valueChanged触发方法,保证同一时间只有一个开关开启,同时同步当前选中的分类:
// 剩余5个分类的开关输出口 @IBOutlet weak var swiWent: UISwitch! @IBOutlet weak var swiSouth: UISwitch! @IBOutlet weak var swiBruns: UISwitch! @IBOutlet weak var swiHome: UISwitch! @IBOutlet weak var swiTelephone: UISwitch! @IBAction func categorySwitchChanged(_ sender: UISwitch) { // 先关闭所有开关 [swiCar, swiWent, swiSouth, swiBruns, swiHome, swiTelephone].forEach { $0?.isOn = false } // 仅开启当前点击的开关 sender.isOn = true // 提前在Storyboard给每个开关设置0-5的tag,对应6个分类,直接映射即可 let categoryMap: [Int: BillCategory] = [ 0: .car, 1: .went, 2: .south, 3: .bruns, 4: .home, 5: .telephone ] currentSelectedCategory = categoryMap[sender.tag] ?? .car }
3. 重写保存按钮逻辑
删除原来无差别全量追加数据的代码,仅向选中分类对应的数组存入数据,增加空值校验避免崩溃:
@IBAction func btnAdd(_ sender: Any) { // 输入合法性校验 guard let billName = txtBill.text, !billName.isEmpty, let billDate = txtDate.text, !billDate.isEmpty, let billImage = imgView.image else { let alert = UIAlertController(title: "保存失败", message: "请填写完整账单信息并拍摄票据", preferredStyle: .alert) alert.addAction(UIAlertAction(title: "确定", style: .default)) present(alert, animated: true) return } // 生成对应分类的账单对象,仅存入目标分类数组 let newBill = Bill(provider: billName, date: billDate, category: currentSelectedCategory) billStorage[currentSelectedCategory]?.append(newBill) // 图片保存逻辑 if let pngData = billImage.pngData(), let compressedImg = UIImage(data: pngData) { UIImageWriteToSavedPhotosAlbum(compressedImg, nil, nil, nil) } // 刷新车辆分类表格 tblCar.reloadData() // 保存成功提示,重置输入项 let successAlert = UIAlertController(title: "保存成功", message: "账单已存入对应分类", preferredStyle: .alert) successAlert.addAction(UIAlertAction(title: "确定", style: .default, handler: { [weak self] _ in self?.txtBill.text = "" self?.txtDate.text = self?.formateDate(date: Date()) self?.imgView.image = nil })) present(successAlert, animated: true) }
4. 更新表格数据源方法
把原来直接读取cars数组的逻辑,替换为读取对应分类的存储数据:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return billStorage[.car]?.count ?? 0 } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) if let targetBill = billStorage[.car]?[indexPath.row] { cell.textLabel?.text = "\(targetBill.provider) \(targetBill.date)" } return cell }
可选优化
- 开关互斥逻辑可以避免用户同时开启多个分类,防止账单重复存储到多个集合
- 如果后续需要切换展示不同分类的账单,只需要把表格数据源切换为
billStorage[对应分类]后调用reloadData()即可,不需要新增页面 - 日期存储建议直接存
Date类型,展示时再用日期格式化器转成字符串,后续做排序、筛选会更方便
内容的提问来源于stack exchange,提问作者JG2022
相关产品推荐
相关产品推荐

