轉自: https://www.jianshu.com/p/539ef116c525
由于項目需求,需要在同一個collectionView上面,實作不同的layout布局。一個是固定item大小,沒有動畫的layout,這個用UICollectionViewFlowLayout就可以實作;另一種是實作水準布局,中間放大,能夠看見兩邊item邊緣的效果。
基于以上需求,在對第一個布局,我直接設定一下UICollectionViewFlowLayout對象的itemSize就可以完成。但是對于第二種的水準移動中間放大的效果,需要自定義layout。下面我就詳細的叙述一下,我在自定義layout時,遇到的坑。
在自定義UICollectionViewFlowLayout的過程中,需要重寫三個方法:
1.- (void)prepareLayout;主要用于相關參數的初始化,比如itemSize,minimumLineSpacing,minimumInteritemSpacing,setScrollDirection(設定滾動方向,sectionInset(整個組的四周邊距);
- (void)prepareLayout{
lastOffset = self.collectionView.contentOffset.x;
//設定每一個cell的尺寸
float itemWidth = SCR_WIDTH - 134;
//設定每一個cell的尺寸
self.itemSize = CGSizeMake(itemWidth, getCellHeight(itemWidth));
self.minimumLineSpacing = 38;
//滑動方向
[self setScrollDirection:UICollectionViewScrollDirectionHorizontal];
CGFloat inset = (self.collectionView.width - itemWidth) / 2.0;
//把item的左右邊切掉,讓item處在螢幕中間位置
// UIEdgeInsetsMake(CGFloat top, CGFloat left, CGFloat bottom, CGFloat right)
self.sectionInset = UIEdgeInsetsMake(0, inset, 0, inset);
}
在這方法中,一定要注意設定sectionInset,因為它決定了你橫向移動首位item的位置。我在一開始,當你發現第一個位置,和最後一個位置,不是自己想要的,你就要開始從這個地方入手考慮了。
2.- (nullable NSArray<__kindof UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect;這個方法是擷取所有item的layoutAttributes;
- (NSArray*)layoutAttributesForElementsInRect:(CGRect)rect{
//計算可見的矩形框
CGRect visiableRect;
visiableRect.size = self.collectionView.frame.size;
visiableRect.origin = self.collectionView.contentOffset;
//1.取得cell原來的UICollectionViewLayoutAttributes
NSArray *array = [super layoutAttributesForElementsInRect:rect];
//螢幕中間的X
CGFloat screenCenterX = self.collectionView.contentOffset.x + self.collectionView.width/2.0;
//2.周遊所有的布局屬性
[array enumerateObjectsUsingBlock:^(UICollectionViewLayoutAttributes * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if (CGRectIntersectsRect(visiableRect, obj.frame)) {
//每個item的centerX
CGFloat itemCenterX = obj.center.x;
//差距越小,縮放越大
//計算縮放比例
CGFloat scale = 1 + 0.25 * (1 - ABS(itemCenterX - screenCenterX) / (self.collectionView.width/2));
obj.transform3D = CATransform3DMakeScale(scale, scale, 1);
}
}];
return array;
}
在這個方法中,可以擷取目前螢幕顯示的顯示的item,也就是視覺上所看到,進而找到需要做動畫的item
3.- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity。這個方法是擷取滑動停止的位置,根據我的需求,我需要在停止的時候,找到離螢幕中間最近的item,并将它顯示在螢幕中間。代碼如下:
- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity{ // 判斷是否為第一個 if (proposedContentOffset.xself.collectionViewContentSize.width-SCR_WIDTH*1.5+self.sectionInset.right) {
return CGPointMake(self.collectionViewContentSize.width-SCR_WIDTH, 0);
}
//1.擷取collectionView最後停留的範圍
CGRect lastRect;
lastRect.origin = proposedContentOffset;
lastRect.size = self.collectionView.frame.size;
//2.取出這個範圍類所有布局屬性
NSArray *array = [self layoutAttributesForElementsInRect:lastRect];
//3.周遊所有布局的屬性
//停止滑動時item應該在的位置
__block CGFloat adjustOffsetX = MAXFLOAT;
//螢幕中間的X
CGFloat screenCenterX = proposedContentOffset.x + self.collectionView.width/2.0;
[array enumerateObjectsUsingBlock:^(UICollectionViewLayoutAttributes * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if (ABS(obj.center.x - screenCenterX) < ABS(adjustOffsetX)) {
adjustOffsetX = obj.center.x - screenCenterX;
}
}];
return CGPointMake(proposedContentOffset.x + adjustOffsetX, proposedContentOffset.y);
}
以上3個方法,可以基本完成一個水準滑動的中間放大的layout的要求,在對兩個layout的切換,隻需要設定setCollectionViewLayout:方法就行了。對于我現在的任務來說,我目前無法完成,滑動速度的控制。
作者:EmptyWalker
連結:https://www.jianshu.com/p/539ef116c525
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯系作者獲得授權并注明出處。