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

UITableView删除单元格未消失却被剩余单元格复制替换问题求助

问题修复方案

核心错误原因

你的猜测并不正确,numberOfRowsInSection是动态返回spotsList的长度,不需要手动修改赋值。问题实际出在以下两处逻辑:

  • Firebase的.value监听回调中,每次收到数据快照时直接向spotsList追加元素,没有先清空旧数据。删除操作触发数据库更新后,回调会将剩余的有效数据再次追加到原有数组中,导致数组存在重复元素,表现为单元格被拷贝。
  • 删除逻辑中reloadData调用时机混乱:你在removeValue的异步回调外部提前调用了reloadData,此时本地数组还未修改、数据库删除请求也未完成;同时回调中同时混用deleteRows和reloadData,反而会导致界面状态和数据源不同步。

修复后代码

import UIKit
import Firebase
import FirebaseAuth

class SpotRemove: ViewController, UITableViewDelegate, UITableViewDataSource {

    @IBOutlet weak var tblSpots: UITableView!
    var spotsList = [ArtistModel]()
    var refSpots: DatabaseReference?
    
    public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return spotsList.count
    }
    
    public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! ViewControllerTableViewCell
        let spot = spotsList[indexPath.row]
        cell.lblName.text = spot.type
        cell.lblGenre.text = spot.avail
        cell.lblPrice.text = spot.price
        return cell
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        tblSpots.allowsMultipleSelectionDuringEditing = true
        refSpots = Database.database().reference().child("Spots")
        refSpots?.observe(.value, with: { [weak self] (snapshot) in
            guard let self = self else { return }
            // 先清空旧数据源,再添加新数据
            self.spotsList.removeAll()
            if snapshot.childrenCount > 0 {
                let userID = Auth.auth().currentUser?.uid
                for spots in snapshot.children.allObjects as! [DataSnapshot] {
                    let spotKey = spots.key
                    let spotObject = spots.value as? [String: AnyObject]
                    let spotType = spotObject?["Type"]
                    let spotAvail = spotObject?["Availability"]
                    let spotPrice = spotObject?["Price"]
                    let spotID = spotObject?["UserID"]
                    
                    let spot = ArtistModel(id: spotID as! String?, avail: spotAvail as! String?, type: spotType as! String?, price: spotPrice as! String?, key: spotKey as! String?)
                    if userID == spotID as? String {
                        self.spotsList.append(spot)
                    }
                }
            }
            // 无数据时也需要刷新界面,UI操作统一调度到主队列
            DispatchQueue.main.async {
                self.tblSpots.reloadData()
            }
        })
    }
    
    func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
        return true
    }
    
    func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
        guard editingStyle == .delete else { return }
        let spot = self.spotsList[indexPath.row]
        guard let spotKey = spot.key else { return }
        Database.database().reference().child("Spots").child(spotKey).removeValue { (error, ref) in
            if let error = error {
                print("Failed to delete message:", error)
                return
            }
            // 此处不需要手动修改数组和刷新界面,Firebase的监听回调会自动更新数据源并刷新
        }
    }
}

补充说明

修复后删除操作触发后,Firebase的监听会自动收到更新后的数据,清空旧数组重新加载后刷新界面,不会再出现重复单元格的问题。如果需要保留删除动画,也可以在删除回调中手动更新本地数组和界面,此时只要避免deleteRows和reloadData混用即可。

内容的提问来源于stack exchange,提问作者jspiezio

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 13:24:05