You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

运行时根据输入文本计算自定义CollectionView Cell的尺寸

针对MessageKit自定义输入状态Cell的高度适配方案

核心思路

自定义输入状态Cell的高度需根据文本内容动态计算,关键是重写MessageKit的尺寸计算方法,结合文本宽高计算逻辑实现适配。

具体实现步骤

1. 自定义Cell的布局配置

先在自定义输入状态Cell中配置好文本控件的约束与属性,确保文本能自动换行:

class CustomTypingCell: TypingIndicatorCell {
    let messageLabel = UILabel()
    
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        setupLabelConstraints()
    }
    
    private func setupLabelConstraints() {
        messageLabel.numberOfLines = 0
        messageLabel.lineBreakMode = .byWordWrapping
        messageLabel.font = UIFont.systemFont(ofSize: 16)
        
        contentView.addSubview(messageLabel)
        messageLabel.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            messageLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16),
            messageLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16),
            messageLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 8),
            messageLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -8)
        ])
    }
    
    func updateText(_ text: String) {
        messageLabel.text = text
    }
}

2. 重写尺寸计算方法

在MessagesViewController中,重写Cell尺寸计算方法,结合文本内容算出动态高度:

override func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    guard let item = messagesCollectionView.messagesDataSource?.messageItem(at: indexPath, in: messagesCollectionView) else {
        return super.collectionView(collectionView, layout: collectionViewLayout, sizeForItemAt: indexPath)
    }
    
    // 判断是否为输入状态Cell
    if item is TypingIndicatorItem {
        let displayText = yourTypingTextSource // 替换为你的输入文本数据源
        let maxTextWidth = view.bounds.width - 32 // 对应Cell内label的左右16pt间距
        // 计算文本高度
        let textHeight = displayText.calculateHeight(with: maxTextWidth, font: UIFont.systemFont(ofSize: 16))
        // 返回Cell总高度:文本高度+上下内边距
        return CGSize(width: view.bounds.width, height: textHeight + 16)
    }
    
    return super.collectionView(collectionView, layout: collectionViewLayout, sizeForItemAt: indexPath)
}

// 给String扩展文本高度计算方法
extension String {
    func calculateHeight(with constrainedWidth: CGFloat, font: UIFont) -> CGFloat {
        let rect = CGSize(width: constrainedWidth, height: .greatestFiniteMagnitude)
        let boundingBox = self.boundingRect(with: rect, options: .usesLineFragmentOrigin, attributes: [.font: font], context: nil)
        return ceil(boundingBox.height)
    }
}

3. 文本更新时刷新布局

当其他用户输入文本变化时,更新数据源并刷新Cell:

func refreshTypingCell(with newText: String) {
    yourTypingTextSource = newText
    // 定位输入状态Cell的位置(通常在最后一个section的第一个item)
    let indexPath = IndexPath(item: 0, section: messagesCollectionView.numberOfSections - 1)
    messagesCollectionView.reloadItems(at: [indexPath])
    messagesCollectionView.layoutIfNeeded()
}

4. 数据源传递文本

在MessagesDataSource的实现中,给自定义Cell传入文本:

func typingIndicatorView(for messagesCollectionView: MessagesCollectionView) -> TypingIndicatorView {
    let cell = messagesCollectionView.dequeueReusableCell(CustomTypingCell.self, for: IndexPath(item: 0, section: messagesCollectionView.numberOfSections - 1))
    cell.updateText(yourTypingTextSource)
    return cell
}

注意事项

  • 计算文本高度时,要保证使用的字体与Cell内Label的字体完全一致,避免高度偏差
  • 如果Cell内还有加载动画等其他元素,计算总高度时要加上这些元素的高度和间距
  • 文本频繁变化时可添加防抖处理(比如延迟0.2秒再刷新),避免布局抖动

自定义输入状态Cell示例

内容的提问来源于stack exchange,提问作者Vishal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 23:05:17