天天看點

對collectionView切換不同layout,實作不同的layout布局

轉自: https://www.jianshu.com/p/539ef116c525

由于項目需求,需要在同一個collectionView上面,實作不同的layout布局。一個是固定item大小,沒有動畫的layout,這個用UICollectionViewFlowLayout就可以實作;另一種是實作水準布局,中間放大,能夠看見兩邊item邊緣的效果。

對collectionView切換不同layout,實作不同的layout布局

基于以上需求,在對第一個布局,我直接設定一下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

來源:簡書

簡書著作權歸作者所有,任何形式的轉載都請聯系作者獲得授權并注明出處。

繼續閱讀