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

为何相同搜索词需发起两次网络请求后UICollectionView才会刷新数据

问题根因与解决方案

核心根因

  • 自定义布局的heightForCell代理方法逻辑缺陷:你通过SDWebImageManager.loadImage同步返回图片对象,但该方法是异步下载逻辑,首次搜索时GIF预览图未缓存,方法直接返回nil,导致所有cell高度计算为0,最终集合视图collectionViewContentSize.height为0,没有可见绘制区域,不会触发cellForItemAt数据源方法。第二次输入相同搜索词时,图片已经存入SDWebImage缓存,同步返回图片对象即可计算出正确高度,集合视图内容高度正常,触发cell加载。
  • 外部高度约束更新未触发布局:首次搜索后heightChanged返回高度为0,你将父容器的collectionViewHeightConstraint设为0后未立即触发父视图布局,GIF集合视图本身被压缩为0高度,即使后续有数据也无法展示。

修复方案

1. 优先使用接口返回的GIF元数据计算高度(最优方案)

绝大多数GIF搜索服务(如Giphy、Tenor)都会在返回结果中附带每个GIF资源的宽高字段,你可以将宽高信息存在POGIFModel的item属性中,计算cell高度时直接使用元数据计算,无需等待图片下载,彻底解决该问题:

func heightForCell(givenWidth cellWidth: CGFloat, at indexPath: IndexPath!) -> CGFloat {
    guard let item = self.viewModel?.gifModel.items[indexPath.row] else {
        return cellWidth
    }
    // 直接用接口返回的宽高计算
    let scaleFactor = item.gifHeight / item.gifWidth
    let imageViewToHighlightedViewSpacing: CGFloat = 4
    return cellWidth * scaleFactor + imageViewToHighlightedViewSpacing
}

2. 修复约束更新逻辑

修改外部高度约束的代理实现,更新约束后立即触发父视图布局,确保集合视图的渲染区域正常:

func collectionViewHeightDidChange(_ height: CGFloat) {
    self.collectionViewHeightConstraint.constant = height
    // 新增此行,立即生效约束变更
    self.view.layoutIfNeeded()
}

3. 调整集合视图刷新时序

修改reloadCollectionView方法,等待数据源刷新、布局计算完成后再更新高度,避免拿到未计算完成的contentSize:

func reloadCollectionView() {
    self.GIFCollectionView.reloadData()
    self.GIFCollectionView.collectionViewLayout.invalidateLayout()
    // 异步等待主队列布局计算完成后再获取contentSize
    DispatchQueue.main.async {
        self.GIFCollectionView.layoutIfNeeded()
        self.heightChanged()
    }
}

4. 无元数据时的降级方案

如果接口确实没有返回GIF宽高,你可以预下载当前页所有GIF预览图,全部下载完成后再刷新集合视图:

// 在didFetchGIFsWithSuccess中新增预下载逻辑
func didFetchGIFsWithSuccess() {
    self.activityIndicator.stopAnimating()
    let group = DispatchGroup()
    guard let items = self.viewModel?.gifModel.items else { return }
    items.forEach { item in
        guard let url = item.previewGIFURL else { return }
        group.enter()
        SDWebImageManager.shared().loadImage(with: url, options: .lowPriority, progress: nil) { _, _, _, _, _, _ in
            group.leave()
        }
    }
    group.notify(queue: .main) {
        self.reloadCollectionView()
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 14:24:05