108 lines
4.3 KiB
Swift
108 lines
4.3 KiB
Swift
//
|
|
// 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
|
|
}
|
|
|
|
|
|
}
|