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

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

我的界面

核心问题

你现有代码的逻辑错误直接导致分类存储失效:

  1. 保存按钮的触发逻辑里,没有判断开关状态就把同一条账单追加到了所有分类的数组中,等于所有分类都会存全量数据
  2. 末尾的开关判断代码存在语法错误:cars.append(newCar) = tblCar属于非法赋值,还重复向车辆账单数组追加了数据
  3. 数据模型设计冗余:6个分类的账单字段完全一致,却重复定义了6个结构体,后续维护成本极高
  4. 目前仅实现了车辆分类的开关输出口和表格绑定,其余分类的开关逻辑、数据展示逻辑完全缺失
  5. 所有输入值、图片对象都用强制解包,用户漏填内容、没拍照片时点击保存会直接触发崩溃
修正步骤

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 12:33:30