前言
趁著這幾天學(xué)習(xí)UICollectionView咐鹤,也跟著別人的例子自己做了下關(guān)于UICollectionView應(yīng)用的Demo闪萄。下面要講的就是通過UICollectionView實(shí)現(xiàn)一個(gè)簡單的相冊冈欢。上篇對理論知識已經(jīng)寫得比較完善了薇宠,所以本篇重點(diǎn)講例子的實(shí)現(xiàn)购对。
先看最終效果圖卢未。該相冊只有一行圖片岂座,可以左右滑動(dòng)瀏覽圖片态蒂,并且圖片滑到中間時(shí)會發(fā)生形變,到整個(gè)view的中點(diǎn)時(shí)達(dá)到峰值费什。而且體驗(yàn)更友好的一點(diǎn)是當(dāng)某圖片接近view中點(diǎn)時(shí)會自動(dòng)滑到view的中點(diǎn)钾恢,感覺像被吸過去一樣。
思路
- 1.確定相冊的基本布局通過UICollectionView實(shí)現(xiàn)鸳址;
- 2.自定義UICollectionViewFlowLayout布局視圖瘩蚪。我們可以看出某張圖片的大小(layoutAttribute的transform屬性值)是和它的位置有某種關(guān)系的稿黍。它們之間的算法我們在layoutAttributesForElementsInRect方法里完成疹瘦,從而正確的展示圖片大小。
- 3.注意一定要實(shí)現(xiàn)下面這個(gè)方法巡球,不然滑動(dòng)時(shí)圖片大小是不會發(fā)生大小形變的言沐,因?yàn)槟J(rèn)這個(gè)方法返回NO。只有返回YES時(shí)酣栈,只要該layout哪里有布局屬性發(fā)生變化险胰,便會重新加載該layout,從而實(shí)現(xiàn)實(shí)時(shí)更新layout布局界面矿筝。
// 當(dāng)布局屬性改變時(shí)重新加載layout
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{
return YES;
}
- 4.所謂圖片滑到view中點(diǎn)有吸附感起便,其實(shí)就是判斷某item滑動(dòng)終止時(shí)的位置距離view的center是否達(dá)到了一個(gè)臨界點(diǎn),若達(dá)到了就把該item的位置改為view的中點(diǎn)即可。
上代碼
布局UICollectionView缨睡,搭建相冊的基本界面鸟悴。
// create collectionView
- (void)loadCollectionView
{
_flowLayout = [[YWPhotoFlowLayout alloc] init];
_flowLayout.itemSize = CGSizeMake(200, 200);
_flowLayout.minimumInteritemSpacing = 5.0f;
_flowLayout.minimumLineSpacing = 5.0f;
_flowLayout.sectionInset = UIEdgeInsetsMake(-50.f, 50.f, 5.f, 50.f);
_flowLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
_collectionView = [[UICollectionView alloc] initWithFrame:(CGRect){0,150.f,self.view.frame.size.width, 250} collectionViewLayout:_flowLayout];
_collectionView.backgroundColor = [UIColor lightGrayColor];
_collectionView.dataSource = self;
_collectionView.delegate = self;
[self.view addSubview:_collectionView];
[_collectionView registerClass:[YWPhotoCell class] forCellWithReuseIdentifier:photoCellId];
}
#pragma mark ---- UICollectionViewDataSource
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return _imgsArray.count;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
YWPhotoCell *photoCell = [_collectionView dequeueReusableCellWithReuseIdentifier:photoCellId forIndexPath:indexPath];
photoCell.image = (UIImage *)_imgsArray[indexPath.row];
return photoCell;
}
創(chuàng)建自定義布局類YWPhotoFlowLayout,注釋在代碼中寫得比較詳細(xì)奖年,所以在這不解釋细诸。
#import "YWPhotoFlowLayout.h"
#define MinValue 100.f
@interface YWPhotoFlowLayout ()
@end
@implementation YWPhotoFlowLayout
- (id)init
{
if(self = [super init])
{
}
return self;
}
- (void)prepareLayout
{
[super prepareLayout];
// self.minimumInteritemSpacing = 5.0f;
// self.minimumLineSpacing = 5.0f;
// self.sectionInset = UIEdgeInsetsMake(0.f, 5.f, 5.f, 5.f);
// self.scrollDirection = UICollectionViewScrollDirectionHorizontal;
}
// 當(dāng)布局改變時(shí)重新加載layout
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{
return YES;
}
// 對layoutAttrute根據(jù)需要做調(diào)整,也許是frame,alpha,transform等
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
// 獲取父類原先的(未放大形變的)attrsArray,我們要對其attr的frame屬性做下調(diào)整
NSArray *attrsArray = [super layoutAttributesForElementsInRect:rect];
CGFloat centerX = self.collectionView.frame.size.width*0.5 + self.collectionView.contentOffset.x;
for(UICollectionViewLayoutAttributes *attr in attrsArray)
{
CGFloat length = 0.f;
if(attr.center.x > centerX)
{
length = attr.center.x - centerX;
}
else
{
length = centerX - attr.center.x;
}
CGFloat scale = 1 - length / self.collectionView.frame.size.width;
attr.transform = CGAffineTransformMakeScale(scale, scale);
}
return attrsArray;
}
- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity
{
// 某cell滑動(dòng)停止時(shí)的最終rect
CGRect rect;
rect.origin.x = proposedContentOffset.x;
rect.origin.y = 0.f;
rect.size = self.collectionView.frame.size;
// 計(jì)算collectionView最中心點(diǎn)的x值
CGFloat centerX = proposedContentOffset.x + self.collectionView.frame.size.width * 0.5;
// 獲得super已經(jīng)計(jì)算好的布局屬性
CGFloat offset = 0.0f;
NSArray *attrsArray = [super layoutAttributesForElementsInRect:rect];
for(UICollectionViewLayoutAttributes *attr in attrsArray)
{
if(attr.center.x - centerX > MinValue || centerX - attr.center.x > MinValue)
{
offset = attr.center.x - centerX; // 此刻陋守,cell的center的x和此刻CollectionView的中心點(diǎn)的距離
}
}
proposedContentOffset.x += offset;
return proposedContentOffset;
}
@end
補(bǔ)充
我在寫這個(gè)Demo的時(shí)候遇到了下圖這個(gè)很蛋疼的問題:
背景色為藍(lán)色的是layout震贵,它的布局是OK的。但是自定義的cell位置確實(shí)亂的水评。
這個(gè)問題耽誤了好久猩系,才突然被我找到問題所在了。中燥。寇甸。重寫的初始化方法initWithFrame傳進(jìn)來的frame值的x和y值并不是0,所以在下面一行創(chuàng)建UIImageView的initWithFrame直接傳入這個(gè)frame當(dāng)然會有問題疗涉。把frame改為bounds就OK了拿霉。
感覺這是我個(gè)我很容易犯的毛病,所以記錄下來咱扣。下面是改正完善后的自定義cell的代碼:
#import "YWPhotoCell.h"
@interface YWPhotoCell ()
{
UIImageView *_imageView;
}
@end
@implementation YWPhotoCell
- (id)initWithFrame:(CGRect)frame
{
if(self = [super initWithFrame:frame])
{
_imageView = [[UIImageView alloc] initWithFrame:self.bounds];
// 給UIImageView加個(gè)圖層绽淘,使其有相框的效果
_imageView.layer.borderColor = [UIColor whiteColor].CGColor;
_imageView.layer.borderWidth = 5.0f;
_imageView.contentMode = UIViewContentModeScaleToFill;
[self.contentView addSubview:_imageView];
self.backgroundColor = [UIColor blueColor];
}
return self;
}
#pragma mark ---- setter/getter
- (void)setImage:(UIImage *)image
{
if(_image != image)
{
_image = image;
_imageView.image = image;
}
}
@end