@property (nonatomic) CGFloat minimumLineSpacing; //每一個item之間最小的行間距
@property (nonatomic) CGFloat minimumInteritemSpacing;//每一個item之間最小的列間距
@property (nonatomic) CGSize itemSize; //每一個item的大小
@property (nonatomic) CGSize estimatedItemSize; //每一個item的預測大小
@property (nonatomic) UICollectionViewScrollDirection scrollDirection; // 集合視圖的滾動方向,默認垂直
@property (nonatomic) CGSize headerReferenceSize; //表頭視圖大小
@property (nonatomic) CGSize footerReferenceSize; //表尾視圖大小
@property (nonatomic) UIEdgeInsets sectionInset; //和集合視圖上下左右四邊的間距
使用流式布局很簡單,不需要我們做任何的操作,只需要創建它的實例,然后把它放入創建的集合視圖中即可。然而,流式布局看起來比較的單一,沒有很炫酷的效果。基於此,我們可以在流式布局的基礎上進行一些布局的擴展,比如線式布局等。。。
下面就做一個流式布局和線式布局的切換,點擊時,可以自由切換,使集合視圖的item排列呈現出不同的效果,舉例如下:
1、首先創建一個自定義的單元格類,並附帶創建一個.xib文件,在.xib文件中設置一個UIImageView,將它IBOutLet到對應的類中
2、准備一些圖片素材
3、在ImagesCell類中
.h
#import <UIKit/UIKit.h> @interface ImagesCell : UICollectionViewCell @property (strong,nonatomic)UIImage *image; @end
.m
#import "ImagesCell.h" @interface ImagesCell() @property (weak, nonatomic) IBOutlet UIImageView *imageView; @end @implementation ImagesCell - (void)awakeFromNib { //設置圖像視圖圖層的屬性 self.imageView.layer.borderWidth = 3; self.imageView.layer.borderColor = [[UIColor redColor]CGColor]; self.imageView.layer.cornerRadius = 5; self.imageView.clipsToBounds = YES; //切割邊角 } -(void)setImage:(UIImage *)image { _image = image; //顯示圖片 [_imageView setImage:_image]; } @end
3.在控制器類ViewController類中
#import "ViewController.h" #import "ImagesCell.h" #import "CustomLineLayout.h" @interface ViewController ()<UICollectionViewDataSource,UICollectionViewDelegate> @property (strong,nonatomic)UICollectionView *collectionView; @property (strong,nonatomic)NSMutableArray *images; @end @implementation ViewController static NSString *const reuseIndentifier = @"image"; //懶加載 -(NSMutableArray *)images { if (!_images) { _images = [NSMutableArray array]; for (int i=1; i<=25; i++) { [_images addObject:[NSString stringWithFormat:@"clothes%d",i]]; } } return _images; } - (void)viewDidLoad { [super viewDidLoad]; //創建集合視圖 CGFloat width = self.view.frame.size.width; CGRect rect = CGRectMake(0, 100, width, 200); self.collectionView = [[UICollectionView alloc]initWithFrame:rect collectionViewLayout:[[CustomLineLayout alloc]init]]; //設置數據源和代理 self.collectionView.dataSource = self; self.collectionView.delegate = self; //注冊單元格 [self.collectionView registerNib:[UINib nibWithNibName:@"ImagesCell" bundle:nil] forCellWithReuseIdentifier:reuseIndentifier]; //添加視圖 [self.view addSubview:self.collectionView]; //UICollectionViewLayout:最根本的布局,自定義布局時,完全需要自己重新去寫需要的布局 //UICollectionViewFlowLayout :流水布局,自定義布局時,有時可以在它的布局基礎上再進行擴展布局 } //切換布局方式 -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { if ([self.collectionView.collectionViewLayout isKindOfClass:[CustomLineLayout class]]) { [self.collectionView setCollectionViewLayout:[[UICollectionViewFlowLayout alloc]init] animated:YES]; } else { [self.collectionView setCollectionViewLayout:[[CustomLineLayout alloc]init] animated:YES]; } } #pragma mark - <UICollectionDataSourceDelegate> //返回組數 -(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView { return 1; } //返回個數 -(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { return self.images.count; } //顯示conllectionView的單元格 -(ImagesCell*)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { //設置重用單元格 ImagesCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:reuseIndentifier forIndexPath:indexPath]; //設置cell的內容 cell.image = [UIImage imageNamed:[self.images objectAtIndex:indexPath.item]]; return cell; } //選中item時刪除它 -(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath { //1.先刪除掉對應的模型數據 [self.images removeObjectAtIndex:indexPath.item]; //2.刪除item(刷新UI) [self.collectionView deleteItemsAtIndexPaths:@[indexPath]]; } @end
4、自定義線式布局,它繼承於流式布局,即
在.m文件中設置每一個item的布局屬性如下:
#import "CustomLineLayout.h" //設置item的固定的寬和高 static const CGFloat itemWH = 100; //設置縮放時的有效距離 static const CGFloat activeDistance = 150; //設置縮放因數,值越大,縮放效果越明顯 static const CGFloat scaleFactor = 0.6; @implementation CustomLineLayout //UICollectionViewLayoutAttributes:很重要,布局屬性設置 //每一個cell(item)都有自己的UICollectionViewLayoutAttributes //每一個indexPath都有自己的UICollectionViewLayoutAttributes -(instancetype)init{ if (self = [super init]){ } return self; } //每一次重新布局前,都會准備布局(蘋果官方推薦使用該方法進行一些初始化) -(void)prepareLayout { [super prepareLayout]; //初始化,設置默認的item屬性 self.itemSize = CGSizeMake(itemWH, itemWH); self.scrollDirection = UICollectionViewScrollDirectionHorizontal; self.minimumLineSpacing = itemWH * scaleFactor; //將第一個和最后一個item始終顯示在中間,即分別將它們設置到組頭和組尾的距離 CGFloat inset = (self.collectionView.frame.size.width - itemWH) / 2; self.sectionInset = UIEdgeInsetsMake(0, inset, 0, inset); } //是否要重新刷新布局(只要顯示的item邊界發生改變就重新布局) //只要每一次重新布局內部就會調用下面的layoutAttributesForElementsInRect:獲取所有cell(item)的屬性 -(BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds { return YES; } //用來設置colectionView停止滾動時的那一刻位置,內部會自動調用 #pragma targetContentOffset : 原本colectionView停止滾動時的那一刻位置 #pragma velocity : 滾動的速率,根據正負可以判斷滾動方向是向左還是向右 -(CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity { //1.計算colectionView最終停留的位置 CGRect lastRect; lastRect.origin = proposedContentOffset; lastRect.size = self.collectionView.frame.size; //2.取出這個范圍內的所有item的屬性 NSArray *array = [self layoutAttributesForElementsInRect:lastRect]; //3.計算最終屏幕的中心x CGFloat centerX = proposedContentOffset.x+ self.collectionView.frame.size.width/2; //4.遍歷所有的屬性,通過計算item與最終屏幕中心的最小距離,然后將item移動屏幕的中心位置 CGFloat adjustOffsetX = MAXFLOAT; for (UICollectionViewLayoutAttributes *attris in array) { if (ABS(attris.center.x - centerX) < ABS(adjustOffsetX)) { adjustOffsetX = attris.center.x - centerX; } } //5.返回要移動到中心的item的位置 return CGPointMake(proposedContentOffset.x + adjustOffsetX , proposedContentOffset.y); } //返回需要重新布局的所有item屬性 -(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect { //0.計算可見的矩形框屬性 CGRect visiableRect; visiableRect.size = self.collectionView.frame.size; visiableRect.origin = self.collectionView.contentOffset; //1.取出默認的cell的UICollectionViewLayoutAttributes NSArray *array = [super layoutAttributesForElementsInRect:rect]; //計算屏幕最中心的x(滾出去的所有的item的偏移 + collectionView寬度的一半) CGFloat centerX = self.collectionView.contentOffset.x + self.collectionView.frame.size.width/2; //2.遍歷所有的布局屬性 for(UICollectionViewLayoutAttributes *attrs in array) { //如果遍歷的item和可見的矩形框的frame不相交,即不e是可見的,就直接跳過,只對可見的item進行放縮 if (!CGRectIntersectsRect(visiableRect, attrs.frame)) continue; //每一個item的中心x CGFloat itemCenterX = attrs.center.x; //差距越大,縮放比例越小 //計算每一個item的中心x和屏幕最中心x的絕對值距離,然后可以計算出縮放比例,即 //<1>計算間距/屏幕一半時的比例,得出的數一定<1 //CGFloat ratio = ABS(itemCenterX - centerX) / (self.collectionView.frame.size.width/2); //CGFloat ratio = ABS(itemCenterX - centerX) / 150; //<2>實現放大 //CGFloat scale = 1 + (1 - ratio); //attrs.transform3D = CATransform3DMakeScale(scale, scale, 1.0); //attrs.transform = CGAffineTransformMakeScale(scale, scale); //當item的中心x距離屏幕的中心x距離在有效距離150以內時,item才開始放大 if (ABS(itemCenterX - centerX) <= activeDistance) { //CGFloat ratio = ABS(itemCenterX - centerX) / (self.collectionView.frame.size.width/2); CGFloat ratio = ABS(itemCenterX - centerX) / activeDistance; //<2>實現放大 CGFloat scale = 1 + scaleFactor*(1 - ratio); attrs.transform3D = CATransform3DMakeScale(scale, scale, 1.0); //attrs.transform = CGAffineTransformMakeScale(scale, scale); } else { attrs.transform = CGAffineTransformMakeScale(1, 1); } } return array; } @end
演示結果如下:
流式布局: 切換為線式布局: