UICollectionView Custom Layout Tutorial: A Spinning Wheel을 Swift 5로 작성한 글입니다.
1. UICollectionViewCell
class CollectionViewCell: UICollectionViewCell {
override func apply(_ layoutAttributes: UICollectionViewLayoutAttributes) {
super.apply(layoutAttributes)
guard let layoutAttributes = layoutAttributes as? CollectionViewLayoutAttributes else { return }
layer.anchorPoint = layoutAttributes.anchorPoint
center.y += (layoutAttributes.anchorPoint.y - 0.5) * bounds.height
}
}
2. UICollectionViewLayout
class CollectionViewLayout: UICollectionViewLayout {
let itemSize = CGSize(width: 150, height: 150)
var radius: CGFloat = 500 {
didSet {
invalidateLayout()
}
}
var anglePerItem: CGFloat {
return atan(itemSize.height / radius)
}
override var collectionViewContentSize: CGSize {
return CGSize(width: CGFloat(collectionView!.numberOfItems(inSection: 0)) * itemSize.width,
height: CGRectGetHeight(collectionView!.bounds))
}
var attributesList = [CollectionViewLayoutAttributes]()
override class var layoutAttributesClass: AnyClass {
return CollectionViewLayoutAttributes.self
}
var angleAtExtreme: CGFloat {
return collectionView!.numberOfItems(inSection: 0) > 0
? -CGFloat(collectionView!.numberOfItems(inSection: 0) - 1) * anglePerItem
: 0
}
var angle: CGFloat {
return angleAtExtreme * collectionView!.contentOffset.x / (collectionViewContentSize.width - CGRectGetWidth(collectionView!.bounds))
}
override func prepare() {
super.prepare()
let centerX = collectionView!.contentOffset.x + (CGRectGetWidth(collectionView!.bounds) / 2.0)
let anchorPointY = ((itemSize.height / 2.0) + radius) / itemSize.height
//1
let theta = atan2(CGRectGetWidth(collectionView!.bounds) / 2.0, radius + (itemSize.height / 2.0) - (CGRectGetHeight(collectionView!.bounds) / 2.0))
//2
var startIndex = 0
var endIndex = collectionView!.numberOfItems(inSection: 0) - 1
//3
if (angle < -theta) {
startIndex = Int(floor((-theta - angle) / anglePerItem))
}
//4
endIndex = min(endIndex, Int(ceil((theta - angle) / anglePerItem)))
//5
if (endIndex < startIndex) {
endIndex = 0
startIndex = 0
}
attributesList = (startIndex...endIndex).map { (i) -> CollectionViewLayoutAttributes in
let attributes = CollectionViewLayoutAttributes(forCellWith: IndexPath(item: i, section: 0))
attributes.size = self.itemSize
attributes.center = CGPoint(x: centerX, y: CGRectGetMidY(self.collectionView!.bounds))
attributes.angle = -(self.angle + (self.anglePerItem * CGFloat(i)))
attributes.anchorPoint = CGPoint(x: 0.5, y: anchorPointY)
return attributes
}
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
return attributesList
}
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
return attributesList[safe: indexPath.row]
}
override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
return true
}
}
3. UICollectionViewLayoutAttributes
class CollectionViewLayoutAttributes: UICollectionViewLayoutAttributes {
// 1
var anchorPoint = CGPoint(x: 0.5, y: 0.5)
var angle: CGFloat = 0 {
// 2
didSet {
zIndex = Int(angle * 1000000)
transform = CGAffineTransformMakeRotation(angle)
}
}
// 3
override func copy(with zone: NSZone? = nil) -> Any {
let copiedAttributes: CollectionViewLayoutAttributes = super.copy(with: zone) as! CollectionViewLayoutAttributes
copiedAttributes.anchorPoint = self.anchorPoint
copiedAttributes.angle = self.angle
return copiedAttributes
}
}
'iOS > Swift' 카테고리의 다른 글
[Swift] 무한 배너 만들기(Make a Infinite slide banner with carousel) (0) | 2022.09.20 |
---|---|
[Swift] 배너 만들기(Make a Slide banner with Carousel) (1) | 2022.09.19 |
[Swift] UILabel 생략 부호(... 또는 Ellipsis) 바꾸기 (0) | 2022.09.01 |
[Swift] textViewDidChange(_:) 위치값 초기화 현상 수정 (0) | 2022.08.22 |
[Swift] 화면 눌러서 키보드 내리기 (Tap to hide keyboard) (0) | 2022.08.12 |