// // SPZoomCollectionViewLayout.swift // Thimra // // Created by Overseas on 2025/4/22. // import UIKit class SPZoomCollectionViewLayout: UICollectionViewFlowLayout { let minimumScale: CGFloat = 0.08 // 调整缩放大小 private(set) var currentIndexPath: IndexPath = IndexPath(row: 0, section: 0) private var cellWidth: CGFloat { return itemSize.width + minimumLineSpacing } override func prepare() { super.prepare() scrollDirection = .horizontal self.collectionView?.decelerationRate = .fast let screenWidth = UIScreen.main.bounds.size.width let insetLeft = (screenWidth - self.itemSize.width) / 2 collectionView?.contentInset = UIEdgeInsets(top: 0, left: insetLeft, bottom: 0, right: insetLeft) } override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint { guard let collectionView = collectionView else { return super.targetContentOffset(forProposedContentOffset: proposedContentOffset, withScrollingVelocity: velocity) } let proposedRect = CGRect(x: proposedContentOffset.x, y: 0, width: collectionView.bounds.width, height: collectionView.bounds.height) guard let layoutAttributes = layoutAttributesForElements(in: proposedRect) else { return super.targetContentOffset(forProposedContentOffset: proposedContentOffset, withScrollingVelocity: velocity) } let horizontalCenterX = proposedContentOffset.x + collectionView.bounds.width / 2 var offsetAdjustment = CGFloat.greatestFiniteMagnitude // ydLog(message: "offsetAdjustment = \(offsetAdjustment)") // ydLog(message: "horizontalCenterX = \(horizontalCenterX)") var currentIndexPath: IndexPath = IndexPath(row: 0, section: 0) for attributes in layoutAttributes { let itemHorizontalCenterX = attributes.center.x let distance = itemHorizontalCenterX - horizontalCenterX // ydLog(message: "distance = \(distance)") //离中心距离最近的 if abs(distance) < abs(offsetAdjustment) { offsetAdjustment = distance currentIndexPath = attributes.indexPath } } self.currentIndexPath = currentIndexPath let point = CGPoint(x: proposedContentOffset.x + offsetAdjustment, y: proposedContentOffset.y) // ydLog(message: "proposedContentOffset = \(proposedContentOffset)") // ydLog(message: "offsetAdjustment = \(offsetAdjustment)") // ydLog(message: "point = \(point)") // ydLog(message: "currentIndex = \(currentIndex)") return point } override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { guard let collectionView = collectionView else { return nil } let visibleRect = CGRect(origin: collectionView.contentOffset, size: collectionView.bounds.size) let attributes = super.layoutAttributesForElements(in: rect)?.compactMap { $0.copy() as? UICollectionViewLayoutAttributes } for attribute in attributes ?? [] { let distance = visibleRect.midX - attribute.center.x let normalizedDistance = distance / (collectionView.bounds.width * 0.5) let zoom = 1 - abs(normalizedDistance) * minimumScale //缩放 let scaleTransform = CGAffineTransform(scaleX: zoom, y: zoom) //旋转 let rotationAngle = kSPAngleToRadians(angle: -normalizedDistance * 4) let rotationTransform = CGAffineTransform(rotationAngle: rotationAngle) // 组合旋转和缩放变换 let combinedTransform = rotationTransform.concatenating(scaleTransform) // 应用变换 attribute.transform = combinedTransform var alpha = 1.8 - abs(normalizedDistance) if alpha < 0 { alpha = 0 } else if alpha > 1 { alpha = 1 } attribute.alpha = alpha } return attributes } override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool { return true } }