技术求助:Bottom Sheet中Collection View单元格重叠动画实现
实现Bottom Sheet联动CollectionView的单元格重叠/展开动画
没问题,我来帮你搞定这个Bottom Sheet联动CollectionView的重叠动画效果!核心思路是根据Bottom Sheet的拖拽进度,动态调整CollectionView单元格的缩放、平移和层级,下面是具体的实现步骤:
1. 给你的Bottom Sheet类添加控制参数
首先在ScrollableBottomSheetViewController里添加几个控制动画的属性,方便你后续调整效果:
class ScrollableBottomSheetViewController: UIViewController { // 原有的属性保留... @IBOutlet var pageControl: UIPageControl! @IBOutlet var collection: UICollectionView! @IBOutlet var headerView: UIView! let fullView: CGFloat = 0 var partialView: CGFloat { return UIScreen.main.bounds.height - 50 } let collectionMargin = CGFloat(32) let itemSpacing = CGFloat(10) let itemHeight = CGFloat(333) // 新增动画控制属性 var currentDragProgress: CGFloat = 0.0 // 拖拽进度:0=完全收起,1=完全展开 private let collapsedScale: CGFloat = 0.85 // 收起时单元格的缩放比例 private let collapsedOffsetX: CGFloat = -20 // 收起时单元格的水平偏移量(控制重叠程度) // 原有的方法保留... }
2. 监听Bottom Sheet的拖拽进度
找到你处理拖拽手势的方法(比如handlePanGesture),在里面计算拖拽进度,并触发CollectionView布局刷新:
@objc func handlePanGesture(_ gesture: UIPanGestureRecognizer) { let translation = gesture.translation(in: view) // 计算拖拽进度:从完全展开(progress=1)到完全收起(progress=0) let progress = 1 - (view.frame.minY - fullView) / (partialView - fullView) currentDragProgress = max(0, min(1, progress)) // 限制在0-1范围内 // 保留你原来的Bottom Sheet拖拽位移逻辑(调整view的frame) // ... // 刷新CollectionView布局,触发动画 collection.collectionViewLayout.invalidateLayout() }
3. 自定义CollectionView布局类
创建一个自定义的UICollectionViewFlowLayout,用来动态调整每个单元格的显示状态:
class OverlappingCollectionLayout: UICollectionViewFlowLayout { // 弱引用关联Bottom Sheet控制器,获取拖拽进度 weak var bottomSheetVC: ScrollableBottomSheetViewController? override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { guard let baseAttributes = super.layoutAttributesForElements(in: rect), let sheetVC = bottomSheetVC else { return baseAttributes } let progress = sheetVC.currentDragProgress let currentPage = sheetVC.pageControl.currentPage // 遍历所有单元格属性,逐个调整 return baseAttributes.map { attr in let copiedAttr = attr.copy() as! UICollectionViewLayoutAttributes let itemIndex = copiedAttr.indexPath.item if itemIndex != currentPage { // 非当前页的单元格:根据进度应用缩放+平移 let scale = sheetVC.collapsedScale + (1 - sheetVC.collapsedScale) * progress let offsetX = sheetVC.collapsedOffsetX * (1 - progress) // 调整层级:当前页在最上层,其他页按顺序在后方 copiedAttr.zIndex = currentPage - itemIndex // 应用变换 var transform = CGAffineTransform(scaleX: scale, y: scale) transform = transform.translatedBy(x: offsetX, y: 0) copiedAttr.transform = transform } else { // 当前页的单元格:保持原始状态,层级最高 copiedAttr.transform = .identity copiedAttr.zIndex = Int.max } return copiedAttr } } // 当CollectionView bounds变化时(比如Bottom Sheet拖拽),重新计算布局 override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool { return true } }
4. 替换原有布局并关联控制器
在ScrollableBottomSheetViewController的viewDidLoad方法里,替换原来的默认布局为我们的自定义布局:
override func viewDidLoad() { super.viewDidLoad() // 初始化自定义布局 let overlappingLayout = OverlappingCollectionLayout() overlappingLayout.bottomSheetVC = self // 配置布局参数(和你原来的设置保持一致) overlappingLayout.itemSize = CGSize( width: collection.bounds.width - collectionMargin * 2, height: itemHeight ) overlappingLayout.sectionInset = UIEdgeInsets( top: 0, left: collectionMargin, bottom: 0, right: collectionMargin ) overlappingLayout.minimumLineSpacing = itemSpacing collection.collectionViewLayout = overlappingLayout // 保留你原来的其他初始化代码(注册cell、设置分页、pageControl关联等) // ... }
5. 处理分页切换时的动画
当用户通过pageControl切换页面时,要刷新布局确保新的当前页状态正确:
@IBAction func pageControlChanged(_ sender: UIPageControl) { let targetIndex = IndexPath(item: sender.currentPage, section: 0) collection.scrollToItem(at: targetIndex, at: .centeredHorizontally, animated: true) // 刷新布局,更新所有单元格的状态 collection.collectionViewLayout.invalidateLayout() }
调整效果小贴士
- 你可以修改
collapsedScale(比如改成0.9)来调整收起时单元格的大小 - 修改
collapsedOffsetX(比如改成-30)来调整重叠的程度 - 如果你的CollectionView是垂直方向的,把平移的x轴改成y轴即可
内容的提问来源于stack exchange,提问作者Nasir Khan
相关产品推荐
相关产品推荐

