具有动态高度和宽度的 UITableView 单元格无法正确重用

UITableView Cell with dynamic height and width not getting reused properly

我有一个具有动态高度和宽度的 UITableView 单元格。最初,它工作正常,但是当重新使用旧单元格时,约束设置不正确。我正在停用所有旧约束并再次激活它们。我还调用了 setNeedsLayout()layoutIfNeeded()。但这并没有帮助。

自动高度设置:(我认为这是一个问题)

discussionTableView.rowHeight = UITableViewAutomaticDimension
discussionTableView.estimatedRowHeight = 10

我的 table 查看单元格:

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

重复使用单元格:

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
    }

重新使用单元格之前:

重复使用单元格后:

编辑

当使用 UITextView 而不是 UILabel 作为 messageLabel 时,约束的工作方式非常不同,table 视图需要 2-3 秒才能加载。

更改了 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)

输出:

这是更新单元格的代码,我还在其中添加了时间标签。所以需要的是 UILable,UITextView,UILabel。现在这是 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
        }
    }
}

发件人姓名标签和消息标签中添加了时间标签的当前输出:

我更改了您的代码设置 messageBuble 相对于单元格而不是内容视图的约束:

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

刚刚调用 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

}

您多次添加子视图,但它是可重用的。不要忘记它。 在 .addSubview(....

之前添加下一个代码
contentView.subviews.forEach { [=10=].removeFromSuperview() }

或者只改变视图值,不要每次都添加

您修改约束的方式超出了您的需要。

更好的方法是为您的“气泡”同时创建前导和尾随约束 --- 并更改它们的优先级以确定使用哪一个。

因此,如果是“已收到”消息,我们将前导约束优先级设置为高,将尾随约束优先级设置为低。如果是“已发送”消息,我们执行相反的操作。

试一试:

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]

    }

}

旁注:cellForRowAt:

中不需要这两行
    //discussionChatMessageCell.setNeedsLayout()
    //discussionChatMessageCell.layoutIfNeeded()

编辑 - 如果你真的想在 11 之前支持 iOS...

我建议您像这样子类化“BubbleView”:

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
    }
}

然后像这样使用它:

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

    }
    
}

这将保持“气泡”视图的形状和大小,即使单元格发生变化(例如旋转设备时)也是如此。