效果

效果.png
標(biāo)題是collectionView,但其實(shí)這篇文章說(shuō)的是自定義FlowLayout的事??
大家都知道collectionView關(guān)于元素的事情,更多是于布局決定的。
比如滾動(dòng)方向,元素間距,元素尺寸,布局的組間距,布局的行間距(這些都決定了元素的位置)。
如果要實(shí)現(xiàn)畫(huà)廊效果只需要關(guān)心三個(gè)方法
/**
* 當(dāng)bounds發(fā)生改變(滾動(dòng)就是改變了bounds,改變了可視區(qū))的時(shí)候是否允許刷新布局。
*
* @param newBounds
*
* @return 默認(rèn)返會(huì)NO
*/
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
return YES;
}
/**
* 返回區(qū)域內(nèi)的cell,這個(gè)方法做根據(jù)cell距離中心點(diǎn) 來(lái)縮放
*
* @param rect 區(qū)域
*
* @return 區(qū)域內(nèi)的cell
*/
- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {
//bounds的改變就是可視范圍的改變, 那么我們?nèi)ツ每梢暦秶鷥?nèi)的cell
NSArray *attrs = [super layoutAttributesForElementsInRect:self.collectionView.bounds];
//計(jì)算每一個(gè)cell與中心點(diǎn)的距離, 并通過(guò)cell與中心點(diǎn)距離的改變來(lái)縮放cell
for (UICollectionViewLayoutAttributes *attr in attrs) {
CGFloat delta = fabs((attr.center.x - self.collectionView.contentOffset.x) - self.collectionView.frame.size.width * 0.5);
CGFloat scale = 1 - (delta / self.collectionView.bounds.size.width * 0.5) * 0.55;
attr.transform = CGAffineTransformMakeScale(scale, scale);
}
return attrs;
}
/**
* 計(jì)算最終的偏移量, 在這個(gè)方法里做判斷最小距離自動(dòng)滾動(dòng)到中間效果
*
* @param proposedContentOffset 預(yù)計(jì)偏移
* @param velocity 速率
*
* @return 最終偏移
*/
- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity {
CGFloat collectionW = self.collectionView.bounds.size.width;
CGFloat collectionH = self.collectionView.bounds.size.height;
// 1確定最終的滾動(dòng)位置
CGPoint targetP = [super targetContentOffsetForProposedContentOffset:proposedContentOffset withScrollingVelocity:velocity];
// 2利用滾動(dòng)位置去拿當(dāng)前顯示的所有元素
CGRect rect = CGRectMake(targetP.x, 0, collectionW, collectionH);
NSArray<UICollectionViewLayoutAttributes *> *attrs = [super layoutAttributesForElementsInRect:rect];
// 3獲取所有滾動(dòng)元素距離中心點(diǎn)的最小值
CGFloat minDelta = MAXFLOAT;
for (UICollectionViewLayoutAttributes *attr in attrs) {
CGFloat delta = attr.center.x - targetP.x - collectionW * 0.5;
if (fabs(delta) < fabs(minDelta))
minDelta = delta;
}
// 4用最終的偏移量加最小值 形成自動(dòng)滑動(dòng)的效果
targetP.x += minDelta;
if (targetP.x < 0)
targetP.x = 0;
return targetP;
}
bounds
bounds的origin是可以改變的,取決于有沒(méi)有可視范圍外的可滾動(dòng)區(qū)域。
scrollView的滾動(dòng)是通過(guò)可視范圍的改變(bounds)來(lái)實(shí)現(xiàn)滾動(dòng)的。
想要實(shí)現(xiàn)這個(gè)效果的朋友直接自定義FlowLayout,重寫(xiě)以上三個(gè)方法即可。