【问题标题】:How to assign value to table view cell in completion handler Swift如何在完成处理程序Swift中为表格视图单元格赋值
【发布时间】:2021-08-01 18:03:53
【问题描述】:

可能会因为我对完成以及如何正确使用它们的无知而提出这个问题,但是我一直在研究并且找不到解决我的问题的方法。

我有一个从数据库获取用户数据的函数,然后我想将从数据库获取的值分配给我的表格视图单元格。如果我的 AssignValueToUserObject 方法没有完成,则在我实际获取导致错误的值之前调用我的表格视图单元格。

所以我发现完成是要走的路,到目前为止它对我有用,但是现在我正在使用表格视图单元格,我实际上不能在完成中手动调用表格视图方法,所以我不'不知道如何使用该方法获取数据,然后将值分配给表格视图单元格。

这是我的方法:

extension MoneyGroupViewController {

public func AssignValueToUserObject(completion: @escaping () -> Void) {
    // Get user unique id
    guard let uid = Auth.auth().currentUser?.uid else {
        print("Could not get user id")
        return
    }

    Database.database().reference().child("users").child(uid).observeSingleEvent(of: .value, with: { [self] snapshot in
        if let dictionary = snapshot.value as? [String: AnyObject] {
            
            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
            
            completion() //call once the action is done
        }
    }, withCancel: nil)
} // End AssignValueToUserObject Method
} // End extension

这是我的表格视图单元格函数,它是我的视图控制器:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = groupTableView.dequeueReusableCell(withIdentifier: "MoneyGroupTableViewCell", for: indexPath) as! MoneyGroupTableViewCell
    
    cell.userName.text = userArray[indexPath.row].first_name + " " + userArray[indexPath.row].last_name
    
    if (user.profile_picture != "") {
        let url = URL(string: user.profile_picture!)!
        
        URLSession.shared.dataTask(with: url, completionHandler: { data, _, error in
            DispatchQueue.main.async {
                cell.userImage.layer.cornerRadius = max(cell.userImage.frame.width,  cell.userImage.frame.height)/2
                cell.userImage.layer.borderWidth = 1
                cell.userImage.image = UIImage(data: data!)
            }
        }).resume() // End URLSession
    }
    
    return cell
}

最后,这是我调用该方法的方式:

override func viewDidLoad() {
    super.viewDidLoad()
    
    AssignValueToUserObject {
        // I should call the table view function here but I don't know how
    }
   }

更新

是的,我的单元格应该显示用户的信息(名字、姓氏、电子邮件和可选的个人资料图片)。

我可能想在我的 userArray 中添加“组”中的所有用户,但现在我没有实现该功能,我只是在 userArray 中有同一个用户来测试我的表格视图。

所以 userArray 现在应该只有同一个用户,但我的问题仍然存在,我不知道如何在 表格视图单元格调用之前从数据库中获取用户信息。 p>

【问题讨论】:

  • 显示userArray 的定义以及如何填充它。简短的回答是,在表格视图准备好显示之前,您无法从数据库中获取或下载图像。您需要显示一个空的表格视图或带有占位符信息的单元格,直到您能够加载所需的数据。
  • 如果您的目标是显示用户列表及其信息,请不要先将其编写为与单个用户一起使用,然后再返回并更改以稍后管理多个用户。处理用户列表时会遇到一些问题,如果你这样做,以后会咬你。你最终需要彻底重写。
  • 我明白你的意思,我会听取你的建议,并会首先尝试管理多个用户。那么在加载数据之前,我将如何实现显示空表视图或带有占位符的单元格呢?您介意发布一个表明这一点的答案吗?
  • Firebase 有多种不同的数据库服务。因此,使用 firebase 标记并不能告诉我们您正在使用哪种服务。
  • 我不确定您指的是什么不同的服务,但我使用的是实时数据库。

标签: ios swift xcode firebase


【解决方案1】:

你想错了。

您不应尝试将内容分配给表格视图单元格。您应该将新获取的数据保存到模型中,然后告诉表视图它需要更新。如果您为现有单元格加载了新内容,请使用reloadRows(at:with:) 告诉表格视图重新加载该数据的单元格。然后表格视图将询问其数据源以获取更新的单元格。数据源将根据您更新的数据构建更新的单元格,并正确显示。

请注意,如果您从完成处理程序内部调用 reloadRows(at:with:),则应将调用包装在对 DispatchQueue.main.async() 的调用中,以便在主线程上执行。

编辑

看看你的代码,你有很多问题。

您的表格视图应该显示什么?包含用户信息的单元格? (名字和姓氏、电子邮件地址和头像?

您的 userArray 包含什么?什么类型的结构数组?用户信息?编辑您的问题以包含该信息。

看起来您实际上需要完成 2 个单独的异步操作,然后才能显示有关用户的所有信息:您需要执行数据库获取以获取有关用户的信息(包括他们的个人资料图片的 URL,然后您需要拨打网络电话(至URLSession.shared.dataTask 以下载您的图片。

您的AssignValueToUserObject 函数似乎总是读取单个用户的信息,但您配置表格视图单元格的代码似乎从数组userArray 中获取数据,**除了user.profile_picture 中的** 图像URL ,这将始终适用于您在 AssignValueToUserObject 中引用的单个用户。

简而言之,您的代码非常混乱,如果没有清楚地描述您要做什么,我们无法帮助您解开它。

【讨论】:

  • 好的,你能告诉我如何将数据保存到我的模型中吗?这是我正在努力解决的问题,我应该在 AssignValueToUserObject 方法的完成中还是在其他地方这样做?
  • 查看我的答案的编辑。在我们为您提供帮助之前,您需要提供更多信息(请编辑您的问题以澄清而不是添加 cmets。)
  • 完成,如果您需要任何额外信息,请告诉我。
【解决方案2】:

你不需要在这里使用完成块。

只要你需要你的 tableview 重新加载它的内容,只需使用 UITableView 的实例方法reloadData()。 这可能是您的数据模型发生变化的时候。

调用tableView.reloadData() 将运行所有数据源方法,包括:

cellForItemAt:numberOfRows:

在这种情况下,我假设你应该有这样的用户结构/类:

struct UserData
{
    var first_name : String = ""
    var last_name : String = ""
    var email : String = ""
    var profile_picture : String = ""
}

尝试这样做:

class YourClass : UIViewController {

//Create an Empty User (As you want to implement with single user first)
let user : UserData = UserData()

override func viewDidLoad() {
        super.viewDidLoad()

   AssignValueToUserObject()

    }

方法:

extension MoneyGroupViewController {

public func AssignValueToUserObject() {
    // Get user unique id
    guard let uid = Auth.auth().currentUser?.uid else {
        print("Could not get user id")
        return
    }

    Database.database().reference().child("users").child(uid).observeSingleEvent(of: .value, with: { [weak self] snapshot in
        
        if let dictionary = snapshot.value as? [String: AnyObject] {
            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
        }

        //Reload Your Tableview once you're done Fetching Data from Firestore
        DispatchQueue.main.async {
        self?.YOURTABLEVIEW.reloadData()
     }
    }, withCancel: nil)
 } // End AssignValueToUserObject Method
}

Tableview 的cellForRowAt:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    
    let cell = groupTableView.dequeueReusableCell(withIdentifier: "MoneyGroupTableViewCell", for: indexPath) as! MoneyGroupTableViewCell
    
    cell.userName.text = user.first_name + " " + user.last_name
  
    //Note: You should use "SDWebImage" or "Kingfisher" Library for async image downloading
    if let url = URL(string: user.profile_picture)
    {
        URLSession.shared.dataTask(with: url, completionHandler: { data, _, error in
            DispatchQueue.main.async {
                cell.userImage.layer.cornerRadius = max(cell.userImage.frame.width,  cell.userImage.frame.height)/2
                cell.userImage.layer.borderWidth = 1
                cell.userImage.image = UIImage(data: data!)
            }
        }).resume() // End URLSession
    }
    return cell
}

注意:

  1. 起初 Tableview 将要加载,但数据为空。 (例如:空用户)
  2. AssignValueToUserObject() 将用一些数据填充用户对象。
  3. reloadData() 将调用,它将重新加载 Tableview 并使用新数据填充单元格中的数据。

【讨论】:

  • 这个答案非常接近我想要的,但是当我实现它时,我首先得到没有他们的个人资料图片的用户名,2秒后,我的单元格被一个随机框和所有内容填充否则消失。你知道我为什么会遇到这个问题吗?
  • 我认为 Tableview Cell 中的 ImageView 缺少一些约束。尝试使用 ImageView 的固定高度和宽度,它应该可以工作!
  • 调用reloadData() 有效,但它是多余的,并且会导致用户体验迟缓。这会导致重新加载表格视图中的每个单元格。您最好使用reloadRows(at:with:) 重新加载需要它的特定单元格(并传入需要更新的IndexPaths 数组。)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-11
  • 2021-12-20
相关资源
最近更新 更多