Trying to add a chat function in my app with tableView but whenever i scroll my chats some cells disappear and i am not sure why. Any help is appreciated.
The tableView is connected to dataSource and delegate in the storyboard.
I have created the cell itself in a xib file and depending on if it is a sender message or received message the cell will display different. This is the code for each cell:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "customMessageCell", for: indexPath) as! CustomMessageCell
//Sets cell to message
senderId = messageArray[indexPath.row].fromId
if senderId == Auth.auth().currentUser?.uid as String? {
//Messages We Sent
cell.ourMessageBody.text = messageArray[indexPath.row].messageBody
cell.ourMessageBackground.backgroundColor = UIColor(displayP3Red: 59/255.0, green: 89/255.0, blue: 152/255.0, alpha: 1)
cell.ourMessageBody.textColor = UIColor.white
cell.avatarImageView.isHidden = true
cell.messageBackground.isHidden = true
} else{
//Messages someone else sent
cell.messageBody.text = messageArray[indexPath.row].messageBody
cell.avatarImageView.backgroundColor = UIColor(white: 0.95, alpha: 1)
cell.messageBackground.backgroundColor = UIColor(white: 0.95, alpha: 1)
cell.ourMessageBackground.isHidden = true
//toId ProfileImage
if let imageID = toId{
print(imageID)
let imagesStorageRef = Storage.storage().reference().child("profilepic/").child(imageID)
imagesStorageRef.getData(maxSize: 1*1024*1024, completion: { (data, error) in
if error != nil{
print(error)
return
}
DispatchQueue.main.async {
cell.avatarImageView?.image = UIImage(data: data!)
}
})
}
}
return cell
}
And here is the method for retrieving messages:
func retrieveMessages() {
let testUserOrAd = SwipingView.myAdvertiserVar.advertiser
print("testing result: \(testUserOrAd) your are a User")
//if advertiser
if testUserOrAd == true{
if let testId = self.toId{
let MessageDB = Database.database().reference().child("Users").child(toId!).child("Messages").child(uid!)
MessageDB.observe(.childAdded) { (snapshot) in
let snapshotValue = snapshot.value as! Dictionary<String,String>
let text = snapshotValue["MessageBody"]!
let fromId = snapshotValue["FromId"]
let message = Message()
message.messageBody = text
message.fromId = fromId
self.messageArray.append(message)
self.configureTableView()
self.messageTableView.reloadData()
}
}
}else{
//if user
let MessageDB = Database.database().reference().child("Users").child(uid!).child("Messages").child(toId!)
MessageDB.observe(.childAdded) { (snapshot) in
let snapshotValue = snapshot.value as? [String: AnyObject]
let text = snapshotValue!["MessageBody"] as? String
let fromId = snapshotValue!["FromId"] as? String
let message = Message()
message.messageBody = text
message.fromId = fromId
self.messageArray.append(message)
self.configureTableView()
self.messageTableView.reloadData()
}
}
}
And this is the result:
Your code is wrong because you configure your cells once (in the tableView:cellForRowAt:
method) but when you scroll the table view if the user alternates, the avatarImageView
, messageBackground
and the ourMessageBackground
will be all hidden eventually.
Try to update your code like that:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "customMessageCell", for: indexPath)
cell.ourMessageBackground.backgroundColor = UIColor(displayP3Red: 59/255.0, green: 89/255.0, blue: 152/255.0, alpha: 1)
cell.ourMessageBody.textColor = UIColor.white
cell.avatarImageView.backgroundColor = UIColor(white: 0.95, alpha: 1)
cell.messageBackground.backgroundColor = UIColor(white: 0.95, alpha: 1)
self.tableView(tableView, willDisplay: cell, forRowAt: indexPath)
return cell
}
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
guard let cell = cell as? CustomMessageCell else { return }
//Sets cell to message
let senderId = messageArray[indexPath.row].fromId
if senderId == Auth.auth().currentUser?.uid as String? {
//Messages We Sent
cell.ourMessageBody.text = messageArray[indexPath.row].messageBody
cell.avatarImageView.isHidden = true
cell.messageBackground.isHidden = true
cell.ourMessageBackground.isHidden = false
} else {
//Messages someone else sent
cell.messageBody.text = messageArray[indexPath.row].messageBody
cell.avatarImageView.isHidden = false
cell.messageBackground.isHidden = false
cell.ourMessageBackground.isHidden = true
//toId ProfileImage
if let imageID = toId {
let imagesStorageRef = Storage.storage().reference().child("profilepic/").child(imageID)
imagesStorageRef.getData(maxSize: 1*1024*1024, completion: { (data, error) in
if error != nil{
print(error)
return
}
DispatchQueue.main.async {
guard let c = tableView.cellForRow(at: indexPath) else { return }
c.avatarImageView?.image = UIImage(data: data!)
}
})
}
}
}
Furthermore as the avatar fetching is made in background, the cell may have changed when the data is retrieved. So you have to be sure the cell is still displayed. And you should cache the avatar to avoid to fetch it every time.