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

如何在UICollectionView滚动时为即将居中的UICollectionViewCell应用CGAffineTransform

实现UICollectionView滚动时中心Item动态放大效果

嘿,我完全懂你想要的那种顺滑效果——水平滚动菜单里,靠近中心的项目慢慢放大,居中时达到最大尺寸,离开中心时再平滑缩小回去。我来帮你完善逻辑,覆盖滚动过程中、停止时以及反向滚动的所有场景:

1. 先做好基础布局配置

首先得让UICollectionView的布局支持item滚动到中心,首尾item也能正常居中显示:

// 初始化流式布局
let flowLayout = UICollectionViewFlowLayout()
flowLayout.itemSize = CGSize(width: 120, height: 180) // 按需调整尺寸
flowLayout.minimumLineSpacing = 20
flowLayout.scrollDirection = .horizontal

// 设置内边距,让首尾item也能滚动到屏幕中心
let sideInset = (view.bounds.width - flowLayout.itemSize.width) / 2
collectionView.contentInset = UIEdgeInsets(top: 0, left: sideInset, bottom: 0, right: sideInset)
collectionView.decelerationRate = .fast // 提升滚动顺滑度
collectionView.showsHorizontalScrollIndicator = false

2. 核心滚动监听与缩放逻辑

在你的UICollectionView代理(或ViewController)里实现滚动监听,实时计算每个item和中心的距离,动态调整缩放比例:

// 可自定义的缩放参数
private let maxCenterScale: CGFloat = 1.2 // 中心item的最大缩放倍数
private let normalScale: CGFloat = 0.9    // 非中心item的基础缩放倍数
private let scaleEffectRange: CGFloat = 150 // 超出这个距离就不再缩放(和item尺寸/间距匹配)

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    updateItemScaleTransforms()
}

func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
    // 让滚动停止时自动居中到最近的item
    let layout = collectionView.collectionViewLayout as! UICollectionViewFlowLayout
    let itemTotalWidth = layout.itemSize.width + layout.minimumLineSpacing
    let targetOffsetX = round(targetContentOffset.pointee.x / itemTotalWidth) * itemTotalWidth
    targetContentOffset.pointee.x = targetOffsetX
    
    // 延迟更新确保item完全居中,避免动画卡顿
    DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) {
        self.updateItemScaleTransforms()
    }
}

private func updateItemScaleTransforms() {
    let screenCenterX = collectionView.contentOffset.x + collectionView.bounds.width / 2
    
    for cell in collectionView.visibleCells {
        guard let _ = collectionView.indexPath(for: cell) else { continue }
        
        // 计算cell中心与屏幕可视区域中心的水平距离
        let cellCenterX = cell.center.x
        let distanceToCenter = abs(screenCenterX - cellCenterX)
        
        // 根据距离动态计算缩放比例:越靠近中心,缩放越大
        var currentScale = max(normalScale, maxCenterScale - (distanceToCenter / scaleEffectRange))
        // 限制缩放范围,避免超出预设值
        currentScale = min(maxCenterScale, max(normalScale, currentScale))
        
        // 应用缩放变换,锚点设为中心保证缩放时位置不变
        cell.layer.anchorPoint = CGPoint(x: 0.5, y: 0.5)
        cell.transform = CGAffineTransform(scaleX: currentScale, y: currentScale)
    }
}

3. 额外优化建议

  • 减少重复计算:给自定义cell加个currentScale属性,只有当计算出的缩放比例和当前值不同时,再更新transform,提升性能;
  • 初始状态处理:在collectionView(_:willDisplay:forItemAt:)里给cell初始化transform为normalScale,避免初始显示异常;
  • 更顺滑的动画:如果觉得实时缩放有点生硬,可以给transform变化加个短时长动画:
UIView.animate(withDuration: 0.1) {
    cell.transform = CGAffineTransform(scaleX: currentScale, y: currentScale)
}

(注:scrollViewDidScroll调用频繁,短时长动画不会造成性能问题)

效果说明

  • 滚动过程中,所有可见item会根据和中心的距离实时调整缩放比例,越靠近中心尺寸越大;
  • 松手后,collectionView会自动滚动到最近的item中心,同时该item平滑放大到最大比例;
  • 反向滚动时无需额外处理,因为我们是根据距离绝对值计算缩放比例的,方向不影响逻辑。

这样就能完美实现你想要的“即将滚动至中心时放大,移出时缩小”的效果啦!

内容的提问来源于stack exchange,提问作者Yannis P.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 03:42:06