基于Firebase的Todo App:Collection View数据显示与数量异常问题
问题分析与修复方案
核心问题拆解
- Firebase数据获取逻辑错误:
- 每次快照触发时未清空任务数组,且在遍历文档的循环内多次调用
completion和刷新视图,导致数据源被重复填充,出现单元格数量异常。 - 函数参数
uid未被使用,冗余获取currentUser.uid,逻辑混乱。
- 每次快照触发时未清空任务数组,且在遍历文档的循环内多次调用
- Collection View Cell配置错误:
- 使用静态类属性
TasksViewCell.textLabel设置文本,而非当前cell实例的属性,导致文本无法正确显示,且单元格复用会出现异常。
- 使用静态类属性
- 视图刷新时机错误:
- 在遍历文档的循环内多次调用
reloadData(),导致视图被重复刷新,引发显示混乱。
- 在遍历文档的循环内多次调用
具体修复代码
1. 修复Firebase数据获取函数
func fetchTasks(uid: String, completion: @escaping([Task])-> Void) { Firestore.firestore().collection("tasks") .document(uid) .collection("ongoing_tasks") .order(by: "timestamp") .addSnapshotListener { snapshot, error in if let err = error { print(err) return } guard let snapShotDocuments = snapshot?.documents else { completion([]) return } // 每次快照更新时先清空数组,避免重复添加 var tasks = [Task]() for doc in snapShotDocuments { let data = doc.data() tasks.append(Task(data: data)) } // 遍历完成后一次性返回完整任务列表 completion(tasks) } }
2. 修复数据源更新与视图刷新逻辑
private func fetchTasks() { guard let uid = self.user?.uid else { return } fetchTasks(uid: uid) { fetchedTasks in // 改用实例属性存储数据源(替代静态属性,避免全局状态混乱) self.tasks = fetchedTasks // 数据源更新后统一刷新Collection View self.collectionView.reloadData() } } // TasksViewController内修改数据源为实例属性 var tasks = [Task]()
3. 修复Collection View Cell配置
class TasksViewCell: UICollectionViewCell { // 定义实例化的文本标签(替代静态类属性) let textLabel: UILabel = { let label = UILabel() label.translatesAutoresizingMaskIntoConstraints = false label.font = UIFont.systemFont(ofSize: 16) return label }() var task: Task? { didSet { configure() } } override init(frame: CGRect) { super.init(frame: frame) contentView.addSubview(textLabel) // 添加约束确保标签正确布局 NSLayoutConstraint.activate([ textLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16), textLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16), textLabel.centerYAnchor.constraint(equalTo: contentView.centerYAnchor) ]) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } private func configure() { guard let task = self.task else { return } // 使用当前cell实例的textLabel设置文本 self.textLabel.text = task.text } }
4. 优化Collection View代理方法
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return tasks.count } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! TasksViewCell cell.task = tasks[indexPath.row] return cell }
5. 优化任务保存函数(可选)
@objc func addTaskButtonClicked() { guard let text = NewTaskViewController.textView.text, !text.isEmpty else { makeAlert(titleInput: "Error", messageInput: "Please write your task.") return } guard let currentUid = Auth.auth().currentUser?.uid else { return } let taskId = UUID().uuidString let data: [String: Any] = [ "text": text, "timestamp": Timestamp(date: Date()), "taskId": taskId ] Firestore.firestore().collection("tasks") .document(currentUid) .collection("ongoing_tasks") .document(taskId) .setData(data) { error in if let err = error { print("保存任务失败:\(err)") } else { NewTaskViewController.textView.text = "" } } }
修复关键点总结
- 每次Firebase快照更新时,先清空任务数组,遍历完成后一次性更新数据源并刷新视图,避免重复添加。
- Cell配置使用实例属性而非静态类属性,确保每个单元格的文本与任务正确绑定。
- 改用实例属性存储数据源,减少全局状态带来的逻辑混乱。
- 清理冗余代码,确保函数参数被正确使用。
内容的提问来源于stack exchange,提问作者7mach
相关产品推荐
相关产品推荐

