cover flow 效果
实现的效果
思路: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…