Firestore文档删除后如何同步移除UITableView对应Cell(无需canEditRowAt)
修复外部触发Firestore删除同步UITableView的实现方案
你当前代码存在三个直接导致功能无法运行的问题,按优先级修复即可:
- 每次遍历文档变更时执行
self.DriverData = []清空数据源,会直接抹掉所有已加载的历史数据,完全不符合快照监听的增量更新逻辑 - 自定义的
accepteddriverData结构体没有存储对应Firestore文档的唯一ID,等到.removed回调触发时,根本没法定位到数组中需要删除的元素 - 仅在新增分支调用全量
reloadData,没有实现局部行删除逻辑,也没有处理UI操作必须在主线程的规范
第一步:为数据源结构体增加文档ID映射字段
不需要引入复杂的数组+字典组合,只要给现有结构体加上唯一的文档ID字段,作为云文档和本地数组元素的匹配键,单个数组就完全适配当前业务:
struct accepteddriverData { // 新增:存储对应Firestore文档的唯一ID,用于删除时定位 var documentID: String var driverName: String var userPhone: String var username: String var driverPhone: String }
第二步:修正快照监听逻辑,实现增量删除
你不需要用到canEditRowAt方法,该方法仅用于控制用户左滑交互的删除权限,外部源触发的删除直接操作本地数据源、调用tableView的deleteRows方法即可,和用户交互逻辑完全解耦:
func driverSelected(arg: Bool, completion: @escaping (Bool) -> ()) { guard let driverid = Auth.auth().currentUser?.uid else { return } let firestore = Firestore.firestore() let doc = firestore.collection("offers").whereField("driverUid", isEqualTo: driverid).whereField("selected", isEqualTo: true) doc.addSnapshotListener { [weak self] (querysnap, err) in guard let self = self else { return } if let err = err { print(err.localizedDescription) return } // 移除原先每次循环清空数组的错误逻辑 querysnap?.documentChanges.forEach({ (change) in let snapData = change.document.data() // 统一提取公共字段,减少重复代码 let driverName = snapData["driverName"] as? String ?? "" let userPhone = snapData["userPhone"] as? String ?? "" let username = snapData["userName"] as? String ?? "" let driverPhone = snapData["driverPhone"] as? String ?? "" switch change.type { case .added: let acceptedModel = accepteddriverData( documentID: change.document.documentID, driverName: driverName, userPhone: userPhone, username: username, driverPhone: driverPhone ) self.DriverData.append(acceptedModel) case .removed: // 通过文档ID定位本地数组中需要删除的元素索引 guard let deleteIndex = self.DriverData.firstIndex(where: { $0.documentID == change.document.documentID }) else { return } // 先从本地数据源移除元素 self.DriverData.remove(at: deleteIndex) // 回到主线程,执行tableView的行删除动画 DispatchQueue.main.async { let deleteIndexPath = IndexPath(row: deleteIndex, section: 0) self.DriverOrdersTV.deleteRows(at: [deleteIndexPath], with: .automatic) } case .modified: // 兼容文档更新场景的逻辑 guard let updateIndex = self.DriverData.firstIndex(where: { $0.documentID == change.document.documentID }) else { return } let updatedModel = accepteddriverData( documentID: change.document.documentID, driverName: driverName, userPhone: userPhone, username: username, driverPhone: driverPhone ) self.DriverData[updateIndex] = updatedModel DispatchQueue.main.async { let updateIndexPath = IndexPath(row: updateIndex, section: 0) self.DriverOrdersTV.reloadRows(at: [updateIndexPath], with: .automatic) } } }) // 所有变更处理完成后,在主线程统一刷新一次tableView,避免多线程UI操作崩溃 DispatchQueue.main.async { self.DriverOrdersTV.reloadData() } } completion(arg) }
注意事项
- 所有涉及UITableView的UI操作必须放在主线程执行,否则会触发无意义的崩溃
- 当Firestore文档被删除时,快照监听会自动触发
.removed分支,拿到对应文档ID后直接匹配数组元素删除即可,全程不需要依赖用户交互 - 当前业务场景完全不需要数组和字典结合的结构,避免增加不必要的维护成本
内容的提问来源于stack exchange,提问作者Cloud Music
相关产品推荐
相关产品推荐

