title: UITableView Cell with dynamic height and width not getting reused properly
body: <p>I have a UITableView cell with dynamic height and width. Initially, it works properly, but when reusing an old cell the constraints are not set correctly. I am deactivating all the old constrains and activating them again. I have also called <code>setNeedsLayout()</code> and <code>layoutIfNeeded()</code>. But it's not helping.</p>
  <p>Automatic height setup: (I think this is causing an issue)</p>
  <pre><code>discussionTableView.rowHeight = UITableViewAutomaticDimension
  discussionTableView.estimatedRowHeight = 10
  </code></pre>
  <p>My table view cell:</p>
  <pre><code>class DiscussionChatMessageCell: UITableViewCell {
      
      private let messageLabel: UILabel
      private let senderNameLabel: UILabel
      private let messageBubble: UIView
      
      let screenWidth: CGFloat
      
      override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
          messageLabel = UILabel()
          senderNameLabel = UILabel()
          screenWidth = UIScreen.main.bounds.size.width
          messageBubble = UIView()
          super.init(style: style, reuseIdentifier: reuseIdentifier)
          
          //        self.contentView.backgroundColor = .clear
          
          self.contentView.addSubview(messageBubble)
          messageBubble.translatesAutoresizingMaskIntoConstraints = false
          
          messageBubble.addSubview(senderNameLabel)
          senderNameLabel.translatesAutoresizingMaskIntoConstraints = false
          senderNameLabel.numberOfLines = 0
          senderNameLabel.lineBreakMode = .byCharWrapping
          senderNameLabel.font = UIFont.boldSystemFont(ofSize: 15)
          senderNameLabel.textColor = .white
          
          messageBubble.addSubview(messageLabel)
          messageLabel.translatesAutoresizingMaskIntoConstraints = false
          messageLabel.numberOfLines = 0
          messageLabel.lineBreakMode = .byWordWrapping
          messageLabel.font = UIFont.systemFont(ofSize: 13)
          messageLabel.textColor = .white
          
          NSLayoutConstraint.activate([
              messageBubble.topAnchor.constraint(equalTo: self.contentView.topAnchor, constant: 10),
              messageBubble.bottomAnchor.constraint(equalTo:  self.contentView.bottomAnchor, constant: -10),
              messageBubble.widthAnchor.constraint(lessThanOrEqualToConstant: screenWidth - 100),
              
              senderNameLabel.topAnchor.constraint(equalTo: messageBubble.topAnchor, constant: 10),
              senderNameLabel.leadingAnchor.constraint(equalTo: messageBubble.leadingAnchor, constant: 10),
              senderNameLabel.trailingAnchor.constraint(equalTo: messageBubble.trailingAnchor, constant: -10),
              
              messageLabel.topAnchor.constraint(equalTo: senderNameLabel.bottomAnchor, constant: 10),
              messageLabel.leadingAnchor.constraint(equalTo: messageBubble.leadingAnchor, constant: 10),
              messageLabel.trailingAnchor.constraint(equalTo: messageBubble.trailingAnchor, constant: -10),
              messageLabel.bottomAnchor.constraint(equalTo: messageBubble.bottomAnchor, constant: -10),
          ])
      }
      
      required init?(coder: NSCoder) {
          fatalError(&quot;init(coder:) has not been implemented&quot;)
      }
      
      func configureCell(message: String, isSender: Bool) {
          senderNameLabel.text = &quot;Default Sender&quot;
          messageLabel.text = message
          
          for constraint in messageBubble.constraints {
              //            messageBubble.removeConstraint(constraint)
              constraint.isActive = false
          }
          for constraint in messageLabel.constraints {
              //            messageLabel.removeConstraint(constraint)
              constraint.isActive = false
          }
          for constraint in senderNameLabel.constraints {
              //senderNameLabel.removeConstraint(constraint)
              constraint.isActive = false
          }
          
          NSLayoutConstraint.deactivate([
              messageBubble.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor, constant: -10),
              messageBubble.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor, constant: 10)
          ])
          NSLayoutConstraint.activate([
              messageBubble.topAnchor.constraint(equalTo: self.contentView.topAnchor, constant: 10),
              messageBubble.bottomAnchor.constraint(equalTo:  self.contentView.bottomAnchor, constant: -10),
              messageBubble.widthAnchor.constraint(lessThanOrEqualToConstant: screenWidth - 100),
              
              senderNameLabel.topAnchor.constraint(equalTo: messageBubble.topAnchor, constant: 10),
              senderNameLabel.leadingAnchor.constraint(equalTo: messageBubble.leadingAnchor, constant: 10),
              senderNameLabel.trailingAnchor.constraint(equalTo: messageBubble.trailingAnchor, constant: -10),
              
              messageLabel.topAnchor.constraint(equalTo: senderNameLabel.bottomAnchor, constant: 10),
              messageLabel.leadingAnchor.constraint(equalTo: messageBubble.leadingAnchor, constant: 10),
              messageLabel.trailingAnchor.constraint(equalTo: messageBubble.trailingAnchor, constant: -10),
              messageLabel.bottomAnchor.constraint(equalTo: messageBubble.bottomAnchor, constant: -10),
          ])
          
          messageBubble.backgroundColor = isSender ? accentColor : .gray
          if isSender {
              
              NSLayoutConstraint.activate([
                  messageBubble.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor, constant: -10)
              ])
              
              //            let corners: UIRectCorner  = [.topLeft, .topRight, .bottomLeft]
              //            roundCorners(corners: corners, isSender: isSender)
              
          } else {
              NSLayoutConstraint.activate([
                  messageBubble.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor, constant: 10)
              ])
              
              //            let corners: UIRectCorner  = [.topLeft, .topRight, .bottomRight]
              //            roundCorners(corners: corners, isSender: isSender)
          }
      }
  </code></pre>
  <p>Reusing cell:</p>
  <pre><code>func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -&gt; UITableViewCell {
          guard let discussionChatMessageCell = tableView.dequeueReusableCell(withIdentifier: discussionChatId, for: indexPath) as? DiscussionChatMessageCell else { return UITableViewCell()}
          
          discussionChatMessageCell.configureCell(message: messages[indexPath.row], isSender: isSender[indexPath.row])
  
          discussionChatMessageCell.setNeedsLayout()
          discussionChatMessageCell.layoutIfNeeded()
          
          return discussionChatMessageCell
      }
  </code></pre>
  <p>Before reusing cell:</p>
  <img src="https://i.stack.imgur.com/cqjEw.png" width="100" />
  <p>After reusing cell:</p>
  <img src="https://i.stack.imgur.com/B0Wj2.png" width="100" />
  <img src="https://i.stack.imgur.com/8tqaI.png" width="100" />
  <h1>Edit</h1>
  <p>When using UITextView instead of UILabel for messageLabel, the constraints work very differently and the table view takes 2-3 seconds to load.</p>
  <p>Changed settings for textView</p>
  <pre><code>// messageLabel.numberOfLines = 0
  // messageLabel.lineBreakMode = .byWordWrapping
  messageLabel.isEditable = false
  messageLabel.dataDetectorTypes = .all
  messageLabel.textContainer.lineBreakMode = .byWordWrapping
  messageLabel.setContentCompressionResistancePriority(.required, for: .vertical)
  messageLabel.setContentHuggingPriority(.required, for: .vertical)
  </code></pre>
  <p>Output:</p>
  <img src="https://i.stack.imgur.com/2SF48.png" width="200" />
  <p>Here's the code for the updated cell, where I have also added a time label. So what is needed is
  UILable, UITextView, UILabel. And right now this is UILabel, UILabel, UILabel.</p>
  <pre><code>class DiscussionChatMessageCell: UITableViewCell {
      
      private let messageLabel: UILabel
      private let senderNameLabel: UILabel
      private let messageSentTimeLabel: UILabel
      private let messageBubble: UIView
      
      private var bubbleLeadingConstraint: NSLayoutConstraint!
      private var bubbleTrailingConstraint: NSLayoutConstraint!
      
      override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
          
          messageLabel = UILabel()
          senderNameLabel = UILabel()
          messageSentTimeLabel = UILabel()
          messageBubble = UIView()
          
          super.init(style: style, reuseIdentifier: reuseIdentifier)
          
          self.contentView.addSubview(messageBubble)
          messageBubble.translatesAutoresizingMaskIntoConstraints = false
          
          messageBubble.addSubview(senderNameLabel)
          senderNameLabel.translatesAutoresizingMaskIntoConstraints = false
          senderNameLabel.numberOfLines = 0
          senderNameLabel.lineBreakMode = .byCharWrapping
          senderNameLabel.font = UIFont.boldSystemFont(ofSize: 15)
          senderNameLabel.textColor = .white
          
          messageBubble.addSubview(messageLabel)
          messageLabel.translatesAutoresizingMaskIntoConstraints = false
          
          //        messageLabel.isEditable = false
          //        messageLabel.dataDetectorTypes = .all
          //        messageLabel.textContainer.lineBreakMode = .byWordWrapping
          
          messageLabel.numberOfLines = 0
          messageLabel.lineBreakMode = .byWordWrapping
          messageLabel.font = UIFont(name: &quot;Helvetica Neue&quot;, size: 13)!
          
          messageBubble.addSubview(messageSentTimeLabel)
          messageSentTimeLabel.translatesAutoresizingMaskIntoConstraints = false
          messageSentTimeLabel.lineBreakMode = .byCharWrapping
          messageSentTimeLabel.numberOfLines = 0
          messageSentTimeLabel.font = UIFont(name: &quot;HelveticaNeue-Italic&quot;, size: 11)!
          
          // set hugging and compression resistance for Name label
          senderNameLabel.setContentCompressionResistancePriority(.required, for: .vertical)
          senderNameLabel.setContentHuggingPriority(.required, for: .vertical)
          
          //        messageLabel.setContentCompressionResistancePriority(.required, for: .vertical)
          //        messageLabel.setContentHuggingPriority(.required, for: .vertical)
          
          // create bubble Leading and Trailing constraints
          bubbleLeadingConstraint = messageBubble.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor, constant: 10)
          bubbleTrailingConstraint = messageBubble.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor, constant: -10)
          
          // priority will be changed in configureCell()
          bubbleLeadingConstraint.priority = .defaultHigh
          bubbleTrailingConstraint.priority = .defaultLow
          
          NSLayoutConstraint.activate([
              
              bubbleLeadingConstraint,
              bubbleTrailingConstraint,
              
              messageBubble.topAnchor.constraint(equalTo: self.contentView.topAnchor, constant: 10),
              messageBubble.bottomAnchor.constraint(equalTo:  self.contentView.bottomAnchor, constant: -10),
              
              messageBubble.widthAnchor.constraint(lessThanOrEqualTo: self.contentView.widthAnchor, constant: -100),
              
              senderNameLabel.topAnchor.constraint(equalTo: messageBubble.topAnchor, constant: 10),
              senderNameLabel.leadingAnchor.constraint(equalTo: messageBubble.leadingAnchor, constant: 10),
              senderNameLabel.trailingAnchor.constraint(equalTo: messageBubble.trailingAnchor, constant: -10),
              
              messageLabel.topAnchor.constraint(equalTo: senderNameLabel.bottomAnchor, constant: 10),
              messageLabel.leadingAnchor.constraint(equalTo: messageBubble.leadingAnchor, constant: 10),
              messageLabel.trailingAnchor.constraint(equalTo: messageBubble.trailingAnchor, constant: -10),
              messageLabel.bottomAnchor.constraint(equalTo: messageSentTimeLabel.topAnchor, constant: -10),
              
              messageSentTimeLabel.leadingAnchor.constraint(equalTo: messageBubble.leadingAnchor, constant: 10),
              messageSentTimeLabel.trailingAnchor.constraint(equalTo: messageBubble.trailingAnchor, constant: -10),
              messageSentTimeLabel.bottomAnchor.constraint(equalTo: messageBubble.bottomAnchor, constant: -10),
              
          ])
          
          // corners will have radius: 10
          messageBubble.layer.cornerRadius = 10
      }
      
      required init?(coder: NSCoder) {
          fatalError(&quot;init(coder:) has not been implemented&quot;)
      }
      
      func configureCell(message: DiscussionMessage, isSender: Bool) {
          senderNameLabel.text = message.userName + &quot; &quot; + message.userCountryEmoji
          
          let date = Date(timeIntervalSince1970: message.messageTimestamp)
          
          let dayTimePeriodFormatter = DateFormatter()
          dayTimePeriodFormatter.timeZone = .current
          
          dayTimePeriodFormatter.dateFormat = &quot;hh:mm a&quot;
          let dateString = dayTimePeriodFormatter.string(from: date)
          
          messageLabel.text = message.message
          
          messageSentTimeLabel.text = dateString
          
          messageLabel.textColor = isSender ? .black : .white
          senderNameLabel.textColor = isSender ? .black : .white
          messageSentTimeLabel.textColor = isSender ? .black : .white
          messageSentTimeLabel.textAlignment = isSender ? .right : .left
          
          bubbleLeadingConstraint.priority = isSender ? .defaultLow : .defaultHigh
          bubbleTrailingConstraint.priority = isSender ? .defaultHigh : .defaultLow
          
          messageBubble.backgroundColor = isSender ? accentColor : .gray
          
          let senderCorners: CACornerMask = [.layerMinXMinYCorner, .layerMaxXMinYCorner, .layerMinXMaxYCorner]
          let nonSenderCorners: CACornerMask =  [.layerMinXMinYCorner, .layerMaxXMinYCorner, .layerMaxXMaxYCorner]
          
          if #available(iOS 11.0, *) {
              messageBubble.layer.maskedCorners = isSender ?
                  // topLeft, topRight, bottomRight
                  senderCorners
                  :
                  // topLeft, topRight, bottomLeft
                  nonSenderCorners
          } else {
              // Fallback on earlier versions
              // All corners will be rounded
          }
      }
  }
  </code></pre>
  <p>Current output with the time label added to sender name label and message label:</p>
  <img src="https://i.stack.imgur.com/XEixl.png" width="200" />