swift iOS 使用UICollectionView完成简单的cover flow 卡片 效果

765 阅读2分钟

cover flow 效果

1.gif

实现的效果

14.jpeg

思路:UICollectionView 自定义 UICollectionViewLayout,这里简单实现,继承 UICollectionViewFlowLayout 就行

1. self.collectionView?.contentInset = UIEdgeInsets(top: (viewHeight - itemHeight )/2, left: 0, bottom: (viewHeight - itemHeight )/2, right: 0) 还没滑动的时候,第一条item在中间显示

2. 实现 override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? 这个方法在滚动的过程当中, 系统会根据需求来调用

3. 实现 override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint UICollectionView 停止滚动时,返回一个新的偏移点坐标

上干货

//

//  PPMatchUserPagerLayout.swift

//  PetPet

//

//  Created by LiZhi on 2023/3/13.

//

  


import UIKit

  


protocol PPMatchUserPagerLayoutDelegate {

    func matchUserPagerLayout(_ layout:PPMatchUserPagerLayout,scrollTo index:Int)

}

  


class PPMatchUserPagerLayout: UICollectionViewFlowLayout {

    var viewHeight = 0.0

    var itemHeight = 0.0

    var delegate :PPMatchUserPagerLayoutDelegate?

    /// 布局之前的准备工作

    override func prepare() {

        super.prepare()

        self.viewHeight = CGRectGetHeight(self.collectionView?.frame ?? .zero)

        self.itemHeight = self.itemSize.height

  


        self.collectionView?.contentInset = UIEdgeInsets(top: (viewHeight - itemHeight - self.minimumInteritemSpacing )/2, left: 0, bottom: (viewHeight - itemHeight )/2, right: 0)

    }

  


    ///    当bounds发生变化的时候是否应该重新进行布局

    override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {

        return true

    }

  


    /// 这个方法在滚动的过程当中, 系统会根据需求来调用

    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {

//    https://juejin.cn/post/6940140043042291748

        // 1.获取该范围内的布局数组

        let attributes = super.layoutAttributesForElements(in: rect)

        // 2.计算出整体中心点的 x 坐标

//        let centerX = collectionView!.contentOffset.x + collectionView!.bounds.width / 2

        let centerY = collectionView!.contentOffset.y + collectionView!.bounds.height / 2

  


        // 3.根据当前的滚动,对每个 cell 进行相应的缩放

        attributes?.forEach { attr in

            // 获取每个 cell 的中心点,并计算这俩个中心点的偏移值

//            let pad = abs(centerX - attr.center.x)

            let pad = abs(centerY - attr.center.y)

  


             // 如何计算缩放比?我的思路是,距离越小,缩放比越小,缩放比最大是1,当俩个中心点的 x 坐标

             // 重合的时候,缩放比就为 1.

  


             // 缩放因子

            let factor = 0.0015//0.0009

             // 计算缩放比

             let scale = 1 / (1 + pad * CGFloat(factor))

            

            attr.transform = CGAffineTransform(scaleX: scale, y: scale)

        }

        // 4.返回修改后的 attributes 数组

        attributes?.forEach { attr in

        }

        return attributes

    }

  


    ///  UICollectionView 停止滚动时,返回一个新的偏移点坐标(UICollectionView不通过设置 isPagingEnabled 来实现分页效果,)proposedContentOffset: 将要停止的点

    override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {

        var offset = proposedContentOffset

        

        let rect = CGRect(origin: CGPoint(x: 0, y: offset.y), size: (collectionView?.frame.size ?? CGSize.zero))

        

        // 取出计算好的布局属性

        let currentRects = super.layoutAttributesForElements(in: rect)

        

        // 计算collectionView的centerY值

        let centerY = offset.y + (collectionView?.frame.size.height ?? 0) * 0.5

        

        var minDelta = MAXFLOAT

        

        // 修改布局属性

        if let rects = currentRects {

            for currentLayoutAttributes in rects {

                if fabsf(minDelta) > fabsf(Float(currentLayoutAttributes.center.y - centerY)) {

                    minDelta = Float(currentLayoutAttributes.center.y - centerY)

                }

            }

        }

        

        offset.y += CGFloat(minDelta)

        

        // 四舍五入取整

        let offsetIndex = Int( round((offset.y + (viewHeight - itemHeight - self.minimumInteritemSpacing)/2 ) / (itemHeight + minimumInteritemSpacing)))

        print("---- offsetIndex = ",offsetIndex)

        self.delegate?.matchUserPagerLayout(self, scrollTo: offsetIndex)

        return offset

    }

  


  

}

参考demo github.com/panghaijiao… 参考 :www.jianshu.com/p/ad8add005…