原文// github: https://github.com/jasnig
// 簡書: http://www.reibang.com/p/b84f4dd96d0c
實現(xiàn)代碼
class LineLayout: UICollectionViewFlowLayout {
// 用來緩存布局
private var layoutAttributes: [UICollectionViewLayoutAttributes] = []
var contentOffsetBeginX:CGFloat = 0.0
override func prepare() {
super.prepare()
scrollDirection = .horizontal
self.minimumLineSpacing = 10
self.minimumInteritemSpacing = 10
let Insetwith = UIScreen.main.bounds.size.width / 2 - itemSize.width / 2
self.sectionInset = UIEdgeInsets.init(top: 0, left: Insetwith, bottom: 0, right: Insetwith)
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
layoutAttributes = []
let superLayoutAttributes = super.layoutAttributesForElements(in: rect)!
let collectionViewCenterX = collectionView!.bounds.width * 0.5
superLayoutAttributes.forEach { (attributes) in
let copyLayout = attributes.copy() as! UICollectionViewLayoutAttributes
// 和中心點的橫向距離差
let deltaX = fabs( copyLayout.center.x - collectionView!.contentOffset.x - collectionViewCenterX)
// 計算屏幕內(nèi)的cell的transform
let precent = 1.0 - (deltaX * 0.1) / collectionViewCenterX
copyLayout.alpha = precent
var transform = CATransform3DIdentity
transform = CATransform3DScale(transform, precent, precent, 1)
copyLayout.transform3D = transform
copyLayout.alpha = 1 - (deltaX * 0.5)/collectionViewCenterX
copyLayout.zIndex = 1
layoutAttributes.append(copyLayout)
}
return layoutAttributes
}
/** 返回true將會標記collectionView的data為 'dirty'
* collectionView檢測到 'dirty'標記時會在下一個周期中更新布局
* 滾動的時候實時更新布局
*/
override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
return true
}
// 此方法已經(jīng)在父類UICollectionViewFlowLayout中
// 已經(jīng)實現(xiàn)了志群,即阻肿,父類中這個方法已經(jīng)能夠產(chǎn)生出一堆
// 決定item如何顯示的attributes對象了
override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {
let distance = proposedContentOffset.x - contentOffsetBeginX
var contentFrame:CGRect = CGRect.init()
if distance > self.itemSize.width / 2 {
contentFrame.origin = CGPoint.init(x: contentOffsetBeginX + self.itemSize.width, y: proposedContentOffset.y)
}else if distance < -self.itemSize.width / 2{
contentFrame.origin = CGPoint.init(x: contentOffsetBeginX - self.itemSize.width, y: proposedContentOffset.y)
}else{
contentFrame.origin = CGPoint.init(x: contentOffsetBeginX , y: proposedContentOffset.y)
}
contentFrame.size = (self.collectionView?.frame.size)!
let array = self.layoutAttributesForElements(in: contentFrame)
var maiCenterX = CGFloat.greatestFiniteMagnitude
let collectionViewCenterX = contentFrame.origin.x + (self.collectionView?.frame.size.width)! * 0.5
array?.forEach({ (attrs) in
if abs(attrs.center.x - collectionViewCenterX) < abs(maiCenterX){
maiCenterX = attrs.center.x - collectionViewCenterX
}
})
return CGPoint.init(x: contentFrame.origin.x + maiCenterX, y: proposedContentOffset.y)
}
}
簡單使用
let layout = LineLayout()
layout.itemSize = CGSize(width: 250.0, height: 400.0)
collectionView?.collectionViewLayout = layout
override func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
layout.contentOffsetBeginX = scrollView.contentOffset.x
}