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

使用MessageKit自定义消息时出现堆叠及滚动错位问题求助

MessageKit自定义消息堆叠&滚动位置错乱问题修复

问题现象

  • 添加新自定义消息时,新消息与旧消息互相堆叠
  • 滚动列表时,消息位置持续错乱,确认与单元格复用(dequeue reusable cell)机制相关

用户提供代码

1. CollectionView单元格配置代码

override open func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    guard let messagesDataSource = messagesCollectionView.messagesDataSource else {
        fatalError("nil data source for messages")
    }

    if isSectionReservedForTypingIndicator(indexPath.section) {
        return super.collectionView(collectionView, cellForItemAt: indexPath)
    }

    let message = messagesDataSource.messageForItem(at: indexPath, in: messagesCollectionView)
    if case .custom = message.kind {
        let cell =  messagesCollectionView.dequeueReusableCell(withReuseIdentifier: "custom cell", for: indexPath) as! MyCustomCell
        cell.Chats = self
        cell.configure(with: message, at: indexPath, and: messagesCollectionView)
        return cell
    }

    return super.collectionView(collectionView, cellForItemAt: indexPath)
}

func numberOfItems(inSection section: Int, in messagesCollectionView: MessagesCollectionView) -> Int {
    return 1
}

2. 自定义尺寸计算器与布局代码

open class CustomMessageSizeCalculator: MessageSizeCalculator {
    public override init(layout: MessagesCollectionViewFlowLayout? = nil) {
        super.init()
        self.layout = layout
    }

    open override func messageContainerSize(for message: MessageType) -> CGSize {
        guard let layout = layout else { return .zero }
        let maxWidth = messageContainerMaxWidth(for: message)
        let contentInset = layout.collectionView?.contentInset ?? .zero
        let inset = layout.sectionInset.left + layout.sectionInset.right + contentInset.left + contentInset.right

        switch message.kind{
            case .custom(let item):
                let constuctedUIV = UIView(CGRect(x:0,y:0, height:100, width:100))

                return CGSize(width: maxWidth, height: constuctedUIV.frame.height + 30)
                break
            default:
                break;
        }

        return CGSize(width: ((maxWidth / 2) - inset), height: ((maxWidth / 4) - inset))
    }
}

open class MyCustomMessagesFlowLayout: MessagesCollectionViewFlowLayout {
    lazy open var customMessageSizeCalculator = CustomMessageSizeCalculator(layout: self)

    open override func cellSizeCalculatorForItem(at indexPath: IndexPath) -> CellSizeCalculator {
        let message = messagesDataSource.messageForItem(at: indexPath, in: messagesCollectionView)
        if case .custom = message.kind {
            return customMessageSizeCalculator
        }
        return super.cellSizeCalculatorForItem(at: indexPath)
    }

    open override func messageSizeCalculators() -> [MessageSizeCalculator] {
        var superCalculators = super.messageSizeCalculators()
        superCalculators.append(customMessageSizeCalculator)
        return superCalculators
    }
}

修复方案

1. 解决单元格复用残留问题

自定义单元格复用时,旧内容未被清理会导致堆叠。在MyCustomCell的configure方法开头,先重置所有子视图:

func configure(with message: MessageType, at indexPath: IndexPath, and messagesCollectionView: MessagesCollectionView) {
    // 清除所有旧子视图,避免复用残留
    contentView.subviews.forEach { $0.removeFromSuperview() }
    
    // 后续根据当前message配置新内容
    // ... 你的自定义UI配置代码
}

2. 修正尺寸计算器逻辑

当前尺寸计算存在两处问题:创建的constuctedUIV未实际参与计算,默认返回的尺寸公式错误。修正后的方法:

open override func messageContainerSize(for message: MessageType) -> CGSize {
    guard let layout = layout else { return .zero }
    let maxWidth = messageContainerMaxWidth(for: message)
    let contentInset = layout.collectionView?.contentInset ?? .zero
    let totalHorizontalInset = layout.sectionInset.left + layout.sectionInset.right + contentInset.left + contentInset.right
    let availableWidth = maxWidth - totalHorizontalInset

    switch message.kind {
    case .custom(let item):
        // 替换为实际自定义内容的高度计算逻辑,示例用固定高度
        let customContentHeight: CGFloat = 100
        return CGSize(width: availableWidth, height: customContentHeight + 30)
    default:
        // 非自定义消息直接复用父类的计算逻辑
        return super.messageContainerSize(for: message)
    }
}

3. 确保单元格与布局正确注册

在视图控制器的viewDidLoad中,完成以下注册:

// 注册自定义单元格
messagesCollectionView.register(MyCustomCell.self, forCellWithReuseIdentifier: "custom cell")
// 设置自定义布局
messagesCollectionView.collectionViewLayout = MyCustomMessagesFlowLayout()

4. 验证数据源逻辑

确保messagesDataSource的实现符合MessageKit规范,每个section对应一条消息,numberOfItems(inSection:)返回1是正确的。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 04:27:47