Swift UITableView单元格数值求和异常:切换单元格后累计值重置
问题:UITableView单元格切换时总价累计重置为0
我做了一个咖啡馆菜单样式的UITableView,支持添加食物并计算总价。现在需要把所有单元格里sumLabel的数值求和显示在底部,但遇到问题:只能统计当前单元格的数值,切换到其他单元格点“+”时,累计值直接从0开始。
我已经做的步骤:
- 在
MainViewController里创建了空数组totalArr: [Int] = [] - 在
CustomCafeTableViewCell里实例化了MainViewController - 在
changeSum()方法里把价格追加到totalArr,问题就出在这。
相关代码
// TableViewController here protocol CustomCellDelegate { func decreaseNumber(cell: CustomCafeTableViewCell, number: Int) func decreaseTotal(cell: CustomCafeTableViewCell, number: Int) } class MainViewController: UITableViewController { let minValue = 0 var totalArr: [Int] = [] let sumCount: UILabel = { let foodCount = UILabel(frame: CGRect(x: 0, y: 0, width: 250, height: 50)) foodCount.textColor = .black foodCount.textAlignment = .center foodCount.translatesAutoresizingMaskIntoConstraints = false return foodCount }() override func viewDidLoad() { super.viewDidLoad() let customView = UIView(frame: CGRect(x: 0, y: 0, width: view.frame.width, height: 50)) customView.backgroundColor = UIColor.systemBackground sumCount.text = "Sum: \(totalArr.reduce(0, +))" customView.addSubview(sumCount) tableView.register(CustomCafeTableViewCell.self, forCellReuseIdentifier: CustomCafeTableViewCell.identifier) tableView.tableFooterView = customView } override func numberOfSections(in tableView: UITableView) -> Int { return 3 } override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { if section == 0 { return "Salads" } else if section == 1 { return "Drinks" } else { return "Desserts" } } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 3 } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: CustomCafeTableViewCell.identifier, for: indexPath) cell.selectionStyle = .none return cell } override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { tableView.deselectRow(at: indexPath, animated: true) } override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat { if section == 2 { return 50 } return 0 } }
// TableViewCell here class CustomCafeTableViewCell: UITableViewCell { var vc = MainViewController() var foodPrice = 10 var totalSum = 0 static let identifier = "cell" private var delegate: CustomCellDelegate? let minValue = 0 @objc func decreaseFunc() { changeQuantity(by: -1) changeSum(by: -foodPrice) } @objc func increaseFunc() { changeQuantity(by: 1) changeSum(by: foodPrice) } func changeQuantity(by amount: Int) { var quality = Int(foodCount.text!)! quality += amount if quality < minValue { quality = 0 foodCount.text = "0" } else { foodCount.text = "\(quality)" } delegate?.decreaseNumber(cell: self, number: quality) } func changeSum(by amount: Int) { var sum = Int(sumLabel.text!)! sum += amount if sum < minValue { sum = 0 sumLabel.text = "0" } else { sumLabel.text = "\(sum)" vc.totalArr.append(amount) print("sum to show \(vc.totalArr.reduce(0, +))") } delegate?.decreaseTotal(cell: self, number: sum) } private let foodLabel: UILabel = { let label = UILabel() label.textColor = .black label.text = "food" label.translatesAutoresizingMaskIntoConstraints = false return label }() private let priceLabel: UILabel = { let label = UILabel() label.translatesAutoresizingMaskIntoConstraints = false return label }() private let increaseButton: UIButton = { let button = UIButton(frame: CGRect(x: 0, y: 0, width: 50, height: 50)) button.setImage(UIImage(systemName: "plus.rectangle"), for: .normal) button.translatesAutoresizingMaskIntoConstraints = false return button }() private let decreaseButton: UIButton = { let button = UIButton(frame: CGRect(x: 0, y: 0, width: 50, height: 50)) button.setImage(UIImage(systemName: "minus.rectangle"), for: .normal) button.translatesAutoresizingMaskIntoConstraints = false return button }() private let foodCount: UILabel = { let foodCount = UILabel() foodCount.text = "0" foodCount.translatesAutoresizingMaskIntoConstraints = false return foodCount }() let sumLabel: UILabel = { let label = UILabel(frame: CGRect(x: 0, y: 0, width: 50, height: 50)) label.translatesAutoresizingMaskIntoConstraints = false label.text = "0" return label }() override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) contentView.addSubview(foodLabel) contentView.addSubview(priceLabel) contentView.addSubview(increaseButton) contentView.addSubview(foodCount) contentView.addSubview(decreaseButton) contentView.addSubview(sumLabel) priceLabel.text = "\(foodPrice) $" increaseButton.addTarget(self, action: #selector(increaseFunc), for: .touchUpInside) decreaseButton.addTarget(self, action: #selector(decreaseFunc), for: .touchUpInside) foodLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 5).isActive = true priceLabel.leadingAnchor.constraint(equalTo: foodLabel.leadingAnchor, constant: 75).isActive = true decreaseButton.leadingAnchor.constraint(equalTo: priceLabel.leadingAnchor, constant: 170).isActive = true foodCount.leadingAnchor.constraint(equalTo: decreaseButton.leadingAnchor, constant: 35).isActive = true increaseButton.leadingAnchor.constraint(equalTo: foodCount.leadingAnchor, constant: 20).isActive = true sumLabel.leadingAnchor.constraint(equalTo: increaseButton.leadingAnchor, constant: 35).isActive = true } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } }
核心问题
你在CustomCafeTableViewCell里写的var vc = MainViewController()是新建了一个全新的MainViewController实例,不是当前屏幕上显示的那个!每个单元格都会创建自己的vc对象,它们的totalArr都是独立的空数组,所以切换单元格时,累计的是新实例里的数组,自然从0开始。
另外还有几个隐性问题:
- 定义了
CustomCellDelegate但没给单元格设置代理,代理方法也没实现 - 没有维护数据源记录每个菜品的数量/小计,UITableView单元格是复用的,滚动后之前的数值会丢失
sumCount标签只会在viewDidLoad时初始化一次,之后数值变化不会更新
修复方案
1. 定义数据模型,持久化菜品状态
先创建Food模型类,用来记录每个菜品的名称、单价、数量、小计,避免单元格复用导致数据丢失:
class Food { let name: String let price: Int var quantity: Int = 0 var subtotal: Int { return price * quantity } init(name: String, price: Int) { self.name = name self.price = price } }
2. 重构MainViewController,维护数据源并实现代理
- 用二维数组作为数据源,对应三个分类
- 实现
CustomCellDelegate方法,更新数据源并计算总价 - 给单元格设置代理,传递当前的
Food模型 - 实时更新底部的
sumCount标签
3. 修改CustomCafeTableViewCell,移除多余的VC实例
- 删除
var vc = MainViewController() - 添加
Food属性,接收外部传递的模型 - 点击按钮时更新模型的数量,通过代理通知ViewController刷新数据和总价
修复后完整代码
MainViewController
protocol CustomCellDelegate: AnyObject { func foodQuantityChanged(for cell: CustomCafeTableViewCell) } class MainViewController: UITableViewController, CustomCellDelegate { // 数据源:三个分类,每个分类下的菜品 var sections: [[Food]] = [ [Food(name: "凯撒沙拉", price: 25), Food(name: "田园沙拉", price: 20), Food(name: "鸡肉沙拉", price: 30)], [Food(name: "美式咖啡", price: 15), Food(name: "橙汁", price: 12), Food(name: "奶茶", price: 18)], [Food(name: "芝士蛋糕", price: 35), Food(name: "提拉米苏", price: 40), Food(name: "冰淇淋", price: 22)] ] let sumCount: UILabel = { let label = UILabel(frame: CGRect(x: 0, y: 0, width: 250, height: 50)) label.textColor = .black label.textAlignment = .center label.font = UIFont.systemFont(ofSize: 16, weight: .medium) label.translatesAutoresizingMaskIntoConstraints = false return label }() override func viewDidLoad() { super.viewDidLoad() setupFooterView() tableView.register(CustomCafeTableViewCell.self, forCellReuseIdentifier: CustomCafeTableViewCell.identifier) updateTotalSum() } private func setupFooterView() { let customView = UIView(frame: CGRect(x: 0, y: 0, width: view.frame.width, height: 50)) customView.backgroundColor = .systemBackground customView.addSubview(sumCount) NSLayoutConstraint.activate([ sumCount.centerXAnchor.constraint(equalTo: customView.centerXAnchor), sumCount.centerYAnchor.constraint(equalTo: customView.centerYAnchor) ]) tableView.tableFooterView = customView } // 计算所有菜品的总价并更新标签 private func updateTotalSum() { let total = sections.flatMap { $0 }.reduce(0) { $0 + $1.subtotal } sumCount.text = "总价: \(total) $" } // MARK: - UITableViewDataSource & Delegate override func numberOfSections(in tableView: UITableView) -> Int { return sections.count } override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { let titles = ["沙拉", "饮品", "甜点"] return titles[section] } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return sections[section].count } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: CustomCafeTableViewCell.identifier, for: indexPath) as! CustomCafeTableViewCell cell.selectionStyle = .none let food = sections[indexPath.section][indexPath.row] cell.configure(with: food) cell.delegate = self return cell } // MARK: - CustomCellDelegate func foodQuantityChanged(for cell: CustomCafeTableViewCell) { guard let indexPath = tableView.indexPath(for: cell) else { return } let food = sections[indexPath.section][indexPath.row] cell.sumLabel.text = "\(food.subtotal)" updateTotalSum() } }
CustomCafeTableViewCell
class CustomCafeTableViewCell: UITableViewCell { weak var delegate: CustomCellDelegate? var currentFood: Food? static let identifier = "cell" private let minValue = 0 @objc func decreaseFunc() { guard let food = currentFood else { return } if food.quantity > minValue { food.quantity -= 1 updateCellUI() delegate?.foodQuantityChanged(for: self) } } @objc func increaseFunc() { currentFood?.quantity += 1 updateCellUI() delegate?.foodQuantityChanged(for: self) } // 用Food模型配置单元格UI func configure(with food: Food) { currentFood = food foodLabel.text = food.name priceLabel.text = "\(food.price) $" foodCount.text = "\(food.quantity)" sumLabel.text = "\(food.subtotal)" } // 更新单元格的数量和小计标签 private func updateCellUI() { guard let food = currentFood else { return } foodCount.text = "\(food.quantity)" sumLabel.text = "\(food.subtotal)" } // MARK: - UI Components private let foodLabel: UILabel = { let label = UILabel() label.textColor = .black label.font = UIFont.systemFont(ofSize: 16) label.translatesAutoresizingMaskIntoConstraints = false return label }() private let priceLabel: UILabel = { let label = UILabel() label.textColor = .gray label.font = UIFont.systemFont(ofSize: 14) label.translatesAutoresizingMaskIntoConstraints = false return label }() private let increaseButton: UIButton = { let button = UIButton(type: .system) button.setImage(UIImage(systemName: "plus.rectangle"), for: .normal) button.tintColor = .systemBlue button.translatesAutoresizingMaskIntoConstraints = false return button }() private let decreaseButton: UIButton = { let button = UIButton(type: .system) button.setImage(UIImage(systemName: "minus.rectangle"), for: .normal) button.tintColor = .systemBlue button.translatesAutoresizingMaskIntoConstraints = false return button }() private let foodCount: UILabel = { let label = UILabel() label.text = "0" label.textAlignment = .center label.font = UIFont.systemFont(ofSize: 16) label.translatesAutoresizingMaskIntoConstraints = false return label }() let sumLabel: UILabel = { let label = UILabel() label.text = "0" label.textAlignment = .right label.font = UIFont.systemFont(ofSize: 16, weight: .medium) label.translatesAutoresizingMaskIntoConstraints = false return label }() override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) setupUI() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } private func setupUI() { contentView.addSubview(foodLabel) contentView.addSubview(priceLabel) contentView.addSubview(increaseButton) contentView.addSubview(foodCount) contentView.addSubview(decreaseButton) contentView.addSubview(sumLabel) increaseButton.addTarget(self, action: #selector(increaseFunc), for: .touchUpInside) decreaseButton.addTarget(self, action: #selector(decreaseFunc),
相关产品推荐
相关产品推荐

