Swift中如何在completion回调中为UITableViewCell赋值
解决方案
核心逻辑是让UITableView在异步数据拉取完成后再触发渲染,具体修改如下:
1. 调整viewDidLoad逻辑
初始化时将数据源数组置空,避免数据未加载完成时触发的渲染导致崩溃,在异步回调中填充数据源后主动刷新列表:
override func viewDidLoad() { super.viewDidLoad() // 提前设置tableView代理和数据源,若无请补充 groupTableView.delegate = self groupTableView.dataSource = self // 初始数据源为空,不会触发cell渲染逻辑 userArray = [] AssignValueToUserObject { [weak self] in guard let self = self else { return } // 填充数据源,当前为单用户测试场景,后续多用户逻辑直接修改此处即可 self.userArray = [user] // 所有UI操作必须在主线程执行,切换主线程刷新列表 DispatchQueue.main.async { self.groupTableView.reloadData() } } }
2. 完善数据拉取方法的回调逻辑
补充异常场景的回调触发,避免拉取失败时列表一直处于无数据状态,同时替换强解包逻辑避免崩溃:
extension MoneyGroupViewController { public func AssignValueToUserObject(completion: @escaping () -> Void) { // 获取用户id失败时也要触发回调 guard let uid = Auth.auth().currentUser?.uid else { print("Could not get user id") completion() return } Database.database().reference().child("users").child(uid).observeSingleEvent(of: .value, with: { [self] snapshot in // 使用defer保证所有分支都会触发回调 defer { completion() } guard let dictionary = snapshot.value as? [String: AnyObject] else { print("用户数据格式解析失败") return } // 替换强解包为可选默认值,避免异常字段导致崩溃 user.first_name = dictionary["first_name"] as? String ?? "" user.last_name = dictionary["last_name"] as? String ?? "" user.email = dictionary["email"] as? String ?? "" user.profile_picture = dictionary["profile_picture"] as? String }, withCancel: { error in print("数据拉取失败:\(error.localizedDescription)") // 拉取取消/失败时触发回调 completion() }) } }
3. 优化Cell渲染逻辑(可选)
解决cell重用导致的头像错位、异常强解包崩溃问题:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = groupTableView.dequeueReusableCell(withIdentifier: "MoneyGroupTableViewCell", for: indexPath) as! MoneyGroupTableViewCell // 重置重用cell的旧头像,可替换为你的占位图资源 cell.userImage.image = UIImage(named: "avatar_placeholder") let currentUser = userArray[indexPath.row] cell.userName.text = "\(currentUser.first_name) \(currentUser.last_name)" // 安全校验头像链接 if let avatarStr = currentUser.profile_picture, !avatarStr.isEmpty, let avatarUrl = URL(string: avatarStr) { URLSession.shared.dataTask(with: avatarUrl, completionHandler: { data, _, error in guard let data = data, error == nil else { return } DispatchQueue.main.async { // 校验当前cell是否仍对应目标indexPath,避免重用错位 guard let targetCell = tableView.cellForRow(at: indexPath) as? MoneyGroupTableViewCell else { return } targetCell.userImage.layer.cornerRadius = max(targetCell.userImage.frame.width, targetCell.userImage.frame.height)/2 targetCell.userImage.layer.borderWidth = 1 targetCell.userImage.image = UIImage(data: data) } }).resume() } return cell }
内容的提问来源于stack exchange,提问作者Omar
相关产品推荐
相关产品推荐

