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

Swift中Firebase加载媒体消息时Collection View滚动卡顿求助

解决JSQMessage + Firebase聊天APP媒体消息滚动卡顿问题

看起来你遇到的真机滚动卡顿问题,核心原因大多是主线程阻塞和不必要的全局刷新,结合你的代码来看,我整理了几个针对性的优化方案:


1. 立刻修复视频缩略图的同步加载(最紧急)

你的视频代码里用了try? Data(contentsOf: thumnailImageUrl!),这是同步网络请求,会直接阻塞主线程——模拟器网络快感觉不明显,但真机网络波动时,主线程被卡死后,CollectionView滚动必然掉帧。

把这部分换成SDWebImage的异步加载:

else if mediatype == "Video" {
    let thumnailImage = dict["ThumnailImageUrl"] as! String
    let videoUrl = dict["VideoUrl"] as? String ?? ""
    let video = URL(string: videoUrl)
    
    // 用SDWebImage异步加载缩略图
    SDWebImageManager.shared.loadImage(
        with: URL(string: thumnailImage),
        options: [],
        progress: nil
    ) { [weak self] image, _, _, _, _, _ in
        guard let self = self, let image = image else { return }
        let videoItem = JSQVideoMediaItem(fileURL: video!, isReadyToPlay: true, thumbnailImage: image)
        self.showMediaData(
            senderId: senderId,
            displayName: sendername,
            text: "",
            ReceiverId: receiverId,
            Receivername: receivername,
            TimeNumber: time,
            media: videoItem,
            Status: status,
            PhotoUrlString: "",
            ThumnailUrlString: thumnailImage,
            VideoUrlString: videoUrl,
            StatusString: status
        )
    }
}

2. 替换全局reloadData()为单个Cell刷新

你现在每次加载完媒体就调用collectionView.reloadData(),这会让整个CollectionView重新渲染所有Cell,当消息量大时,性能开销极大。应该只刷新新增的那一个Cell:

修改showMediaData里的刷新逻辑:

if newMess.ChatId == self.receiverId! {
    self.Array_Messages.append(newJSQMedia!)
    self.ClassMessages.append(newMess)
    self.finishReceivingMessage()
    
    DispatchQueue.main.async() {
        // 只刷新最后一个新增的Cell
        let indexPath = IndexPath(row: self.Array_Messages.count - 1, section: 0)
        self.collectionView.reloadItems(at: [indexPath])
        // 如果需要自动滚动到底部,保留这行
        self.collectionView.scrollToItem(at: indexPath, at: .bottom, animated: true)
    }
}

图片下载完成后的刷新也要改成单个Cell刷新:

downloader.downloadImage(with: URL(string: PhotoUrl), options: [], progress: nil, completed: { [weak self] (image, data, error, finished) in
    guard let self = self, let image = image else { return }
    DispatchQueue.main.async {
        // 找到对应的消息索引,刷新单个Cell
        if let index = self.Array_Messages.firstIndex(where: {
            // 根据消息的senderId和时间匹配,或者你可以给Message加个唯一标识
            $0.senderId == senderId && $0.date == Date(timeIntervalSince1970: TimeInterval(time.doubleValue))
        }) {
            photo?.image = image
            let indexPath = IndexPath(row: index, section: 0)
            self.collectionView.reloadItems(at: [indexPath])
        }
    }
})

3. 优化SDWebImage的图片加载策略

避免加载过大的图片到内存,给SDWebImage添加resize选项,让图片适配Cell的大小:

// 图片下载时添加resize配置
let options: SDWebImageOptions = [.scaleDownLargeImages]
downloader.downloadImage(with: URL(string: PhotoUrl), options: options, progress: nil, completed: { ... })

同时确保SDWebImage的内存缓存策略合理,默认的就不错,但可以在App启动时配置:

SDImageCache.shared.config.maxMemoryCost = 100 * 1024 * 1024 // 100MB内存缓存上限

4. 检查JSQMediaItem的内存占用

JSQPhotoMediaItem和JSQVideoMediaItem如果持有过大的图片/视频数据,会快速消耗内存,导致滚动卡顿。可以:

  • 确保视频缩略图是压缩后的小尺寸(建议不超过300x300)
  • 发送图片时,先在本地压缩再上传到Firebase,避免加载原图
  • 对于已加载的媒体,在Cell被复用或离开屏幕时,及时释放不必要的引用

这些优化做完后,真机的滚动流畅度应该会有明显提升。另外,你可以用Xcode的Instruments工具(选择Core Animation或Memory)来监控卡顿和内存泄漏的具体点,更精准地解决问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 09:37:45