KMAdvertisementFlowLayout.swift 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. //
  2. // KMAdvertisementFlowLayout.swift
  3. // KMAdvertisement_iOS
  4. //
  5. // Created by lizhe on 2022/12/2.
  6. //
  7. import UIKit
  8. class KMAdvertisementFlowLayout: UICollectionViewFlowLayout {
  9. ///单元格的宽度
  10. private let K_CELL_WIDTH: CGFloat = 260
  11. override init() {
  12. super.init()
  13. itemSize = CGSize(width: K_CELL_WIDTH, height: 360) //cell的大小自定义
  14. scrollDirection = .horizontal
  15. minimumLineSpacing = 0
  16. }
  17. required init?(coder aDecoder: NSCoder) {
  18. fatalError("init(coder:) has not been implemented")
  19. }
  20. override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
  21. return true
  22. }
  23. /// 不想频繁去取值, 直接提出来一个属性, 然后直接计算出半宽
  24. var collectionViewFrameWidth: CGFloat = 0 {
  25. didSet {
  26. collectionViewFrameWidthHalf = collectionViewFrameWidth / 2
  27. }
  28. }
  29. var collectionViewFrameWidthHalf: CGFloat = 0
  30. var collectionViewFrameHeight: CGFloat = 0
  31. /// 不想每次都去创建这个属性,所以提出来
  32. var visibleRect = CGRect.zero
  33. /// 把相关属性初始化放到一起,初始化一次就可以了,在
  34. func needViewInfo() {
  35. if collectionViewFrameWidth != collectionView?.frame.width {
  36. collectionViewFrameWidth = collectionView?.frame.width ?? 0
  37. let offsetXY = (collectionViewFrameWidth - K_CELL_WIDTH) / 2
  38. //在滑动到左右两端的时候, 需要让头尾的cell居中显示,那就需要设置边缘距离, 这个距离就是 屏幕宽-cell宽 除以 2
  39. sectionInset = UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 20)
  40. }
  41. if collectionViewFrameHeight != collectionView?.frame.height {
  42. collectionViewFrameHeight = collectionView?.frame.height ?? 0
  43. }
  44. }
  45. override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
  46. //先做一下相关属性的初始化
  47. needViewInfo()
  48. //可视区域
  49. visibleRect.origin = collectionView?.contentOffset ?? CGPoint.zero
  50. visibleRect.size = collectionView?.frame.size ?? CGSize.zero
  51. //屏幕中线
  52. let centerX = visibleRect.midX
  53. ///获取到可以看见的cell的数组,看不见的cell就不要了 如果这里传入 rect, 那获取到的就是所有缓存中的cell, 并不是全部的总cell个数
  54. let array = super.layoutAttributesForElements(in: visibleRect) ?? []
  55. for attributes in array {
  56. // 随着滑动, centerX 是一直都在改变的, 但是每个cell的 attributes.center.x 是不变的
  57. let distance = abs(centerX - attributes.center.x) //for循环中当前cell和可见区域的中心点的距离
  58. let scaleRate = distance / (collectionView?.bounds.width ?? 0)
  59. // 使用 cos 函数,把缩放的比值转换成一个 1 以为的数值
  60. // 在 scaleRate 后面乘上一个数,就可以控制卡片缩放的剧烈程度
  61. let scale = abs(cos(scaleRate * 0.4))
  62. //scaleRate == 0, 越是接近 centerX 的那个 attribute, 他的缩放就越接近于1(就是cell原来的大小), 可以直接使用这样的缩放,这样的缩放,卡片的大小变化不剧烈
  63. //let scale = abs(cos(scaleRate))
  64. attributes.transform3D = CATransform3DMakeScale(scale, scale, 1)
  65. }
  66. return array
  67. }
  68. override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {
  69. //被推出屏幕的长度 + 屏幕宽度的一半
  70. let horizontalCenter = proposedContentOffset.x + collectionViewFrameWidthHalf
  71. //获取一个当前CollectionView的尺寸大小,然后这个尺寸的 X 就是滑动停止时候的偏移
  72. let targetRect = CGRect(x: proposedContentOffset.x, y: 0.0, width: collectionViewFrameWidth, height: collectionViewFrameHeight)
  73. //用这个方法取得当前能看到的所有的cell
  74. let array = super.layoutAttributesForElements(in: targetRect) ?? []
  75. //记录每次遍历取到的最小的距离中心的距离
  76. var nearestCenterOne = CGFloat(MAXFLOAT)
  77. //记录距离中心最近的那个layoutitem的中心点x
  78. var shouldBeX: CGFloat = 0
  79. //遍历当前数组里面的元素,找到里中心点最近的那个元素,把这个元素的中心位置拿到,然后返回给函数
  80. for layoutAttributes in array {
  81. let currentX = layoutAttributes.center.x
  82. let currentDistance = abs(currentX - horizontalCenter)
  83. if currentDistance < nearestCenterOne {
  84. nearestCenterOne = currentDistance
  85. shouldBeX = currentX
  86. }
  87. }
  88. let X = shouldBeX - collectionViewFrameWidthHalf
  89. return CGPoint(x: X , y: proposedContentOffset.y)
  90. }
  91. }