Dracula
Dracula

Reputation: 3090

UITableView Cell with dynamic height and width not getting reused properly

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 setNeedsLayout() and layoutIfNeeded(). But it's not helping.

Automatic height setup: (I think this is causing an issue)

discussionTableView.rowHeight = UITableViewAutomaticDimension
discussionTableView.estimatedRowHeight = 10

My table view cell:

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("init(coder:) has not been implemented")
    }
    
    func configureCell(message: String, isSender: Bool) {
        senderNameLabel.text = "Default Sender"
        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)
        }
    }

Reusing cell:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> 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
    }

Before reusing cell:

After reusing cell:

Edit

When using UITextView instead of UILabel for messageLabel, the constraints work very differently and the table view takes 2-3 seconds to load.

Changed settings for textView

// 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)

Output:

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.

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: "Helvetica Neue", size: 13)!
        
        messageBubble.addSubview(messageSentTimeLabel)
        messageSentTimeLabel.translatesAutoresizingMaskIntoConstraints = false
        messageSentTimeLabel.lineBreakMode = .byCharWrapping
        messageSentTimeLabel.numberOfLines = 0
        messageSentTimeLabel.font = UIFont(name: "HelveticaNeue-Italic", 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("init(coder:) has not been implemented")
    }
    
    func configureCell(message: DiscussionMessage, isSender: Bool) {
        senderNameLabel.text = message.userName + " " + message.userCountryEmoji
        
        let date = Date(timeIntervalSince1970: message.messageTimestamp)
        
        let dayTimePeriodFormatter = DateFormatter()
        dayTimePeriodFormatter.timeZone = .current
        
        dayTimePeriodFormatter.dateFormat = "hh:mm a"
        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
        }
    }
}

Current output with the time label added to sender name label and message label:

Upvotes: 0

Views: 499

Answers (3)

DonMag
DonMag

Reputation: 77691

You are modifying constraints way more than you need to.

A better approach would be to create both Leading and Trailing constraints for your "bubble" --- and change their Priority to determine which one is used.

So, if it's a "Received" message, we set the Leading constraint Priority to High, and the Trailing constraint Priority to Low. If it's a "Sent" message, we do the opposite.

Give this a try:

class DiscussionChatMessageCell: UITableViewCell {
    
    let accentColor: UIColor = .systemYellow
    
    private let messageLabel: UILabel
    private let senderNameLabel: UILabel
    private let messageBubble: UIView
    
    private var bubbleLeadingConstraint: NSLayoutConstraint!
    private var bubbleTrailingConstraint: NSLayoutConstraint!

    // not needed
    //let screenWidth: CGFloat

    // wrong signature
    //override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        
        messageLabel = UILabel()
        senderNameLabel = UILabel()
        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
        
        // set hugging and compression resistance for Name label
        senderNameLabel.setContentCompressionResistancePriority(.required, for: .vertical)
        senderNameLabel.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: messageBubble.bottomAnchor, constant: -10),
            
        ])
        
        // corners will have radius: 10
        messageBubble.layer.cornerRadius = 10
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    func configureCell(message: String, isSender: Bool) {
        senderNameLabel.text = "Default Sender"
        messageLabel.text = message

        bubbleLeadingConstraint.priority = isSender ? .defaultHigh : .defaultLow
        bubbleTrailingConstraint.priority = isSender ? .defaultLow : .defaultHigh

        messageBubble.backgroundColor = isSender ? accentColor : .gray
        
        messageBubble.layer.maskedCorners = isSender ?
            // topLeft, topRight, bottomRight
            [.layerMinXMinYCorner, .layerMaxXMinYCorner, .layerMaxXMaxYCorner]
            :
            // topLeft, topRight, bottomLeft
            [.layerMinXMinYCorner, .layerMaxXMinYCorner, .layerMinXMaxYCorner]

    }

}

Side Note: neither of these lines is needed in cellForRowAt:

    //discussionChatMessageCell.setNeedsLayout()
    //discussionChatMessageCell.layoutIfNeeded()

Edit - if you really want to support iOS prior to 11...

I suggest you subclass your "BubbleView" like this:

class BubbleView: UIView {
    var radius: CGFloat = 0
    var corners: UIRectCorner = []
    var color: UIColor = .clear
    
    lazy var shapeLayer: CAShapeLayer = self.layer as! CAShapeLayer
    
    override class var layerClass: AnyClass {
        return CAShapeLayer.self
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        let path = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
        shapeLayer.path = path.cgPath
        shapeLayer.fillColor = color.cgColor
    }
}

and then use it like this:

class DiscussionChatMessageCell: UITableViewCell {
    
    let accentColor: UIColor = .systemYellow
    
    private let messageLabel: UILabel
    private let senderNameLabel: UILabel
    
    // use custom BubbleView class instead of standard UIView
    private let messageBubble: BubbleView
    
    private var bubbleLeadingConstraint: NSLayoutConstraint!
    private var bubbleTrailingConstraint: NSLayoutConstraint!
    
    // wrong signature - I beliee as of Swift 4.2
    // 'UITableViewCellStyle' has been renamed to 'UITableViewCell.CellStyle'
    //override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        
        messageLabel = UILabel()
        senderNameLabel = UILabel()
        messageBubble = BubbleView()
        
        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.numberOfLines = 0
        messageLabel.lineBreakMode = .byWordWrapping
        messageLabel.font = UIFont.systemFont(ofSize: 13)
        messageLabel.textColor = .white
        
        // set hugging and compression resistance for Name label
        senderNameLabel.setContentCompressionResistancePriority(.required, for: .vertical)
        senderNameLabel.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: messageBubble.bottomAnchor, constant: -10),
            
        ])
        
        // corners will have radius: 10
        messageBubble.radius = 10
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    func configureCell(message: String, isSender: Bool) {
        senderNameLabel.text = "Default Sender"
        messageLabel.text = message
        
        bubbleLeadingConstraint.priority = isSender ? .defaultHigh : .defaultLow
        bubbleTrailingConstraint.priority = isSender ? .defaultLow : .defaultHigh
        
        messageBubble.color = isSender ? accentColor : .gray

        let senderCorners: UIRectCorner = [.topLeft, .topRight, .bottomRight]
        let nonSenderCorners: UIRectCorner =  [.topLeft, .topRight, .bottomLeft]

        messageBubble.corners = isSender ? senderCorners : nonSenderCorners

    }
    
}

That will keep the "bubble" view's shape and size, even when the cell changes (such as when rotating the device).

enter image description here

enter image description here

Upvotes: 1

Dzmitry Sotnikov
Dzmitry Sotnikov

Reputation: 80

You add subviews a lot of times, but it's REUSABLE. Don't forget about it. Add next code before .addSubview(....

contentView.subviews.forEach { $0.removeFromSuperview() }

Or change views values only, don't add it each time

Upvotes: -2

Mat
Mat

Reputation: 6324

I changed your code setting messageBuble constraint relative to the cell instead of the content view:

 messageBubble.topAnchor.constraint(equalTo: self.topAnchor, constant: 10),
 messageBubble.bottomAnchor.constraint(equalTo:  self.bottomAnchor, constant: -10)

the just calling layoutIfNeeded():

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> 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.layoutIfNeeded()
        
return discussionChatMessageCell

}

Upvotes: 0

Related Questions