Search code examples
swiftuitableviewmessage

Swift tableView Cells disappears on scroll


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:

enter image description here


Solution

  • 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.