自定义collocationViewLayout完结瀑布流,的灵活利用

实现瀑布流简单,实现分区瀑布流,并且每个区的瀑布流的列数不一样且有区头和区尾,就不是太容易了。我嫌麻烦不愿意自己写(——>我承认懒,不愿意动脑子 *V*)开始在网上找了好多,都是仅仅一个区的瀑布流,没区头和区尾,完全满足不了我的需求。没办法,产品的需求在那,不能不做吧,于是自己静下心来开始写。

ios UICollectionView实现瀑布流 

1.相信大家都会很熟练使用UITableView了,但是今天给大家 介绍一个iOS6就已经引入的UICollectionView ,功能更加强大,更加灵活,学会使用它,会 帮你解决很多的问题的!它跟UITableView很相似,所以 熟练使用UICollectionView很简单!

ios UICollectionView的使用

UICollectionView的使用有两种方法,一种是继承UICollectionViewController,这个Controller会自带一个UICollectionView;另外一种是作为一个视图放在普通的UIViewController里面。

个人更喜欢第二种。下面采用第二种方式简单介绍一下UICollectionView的使用。

1.UIViewController实现委托,代码如下

@interface YourViewController : UIViewController

2.声明UICollectionView,代码如下

@property(nonatomic,retain)UICollectionView*myCollectionView;

3.初始化UICollectionView,代码如下

- (void)viewDidLoad
{
[super viewDidLoad];
[self initConllectionView];
}
-(void)initConllectionView{
CircleLayout*layout=[[CircleLayout alloc] init];
myCollectionView=[[UICollectionView alloc] initWithFrame:self.view.bounds collectionViewLayout:layout];
[myCollectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"cell"];
myCollectionView.backgroundColor=[UIColor whiteColor];
myCollectionView.delegate=self;
myCollectionView.dataSource=self;
[self.view addSubview:myCollectionView];
[layout release];
}

这里面的CircleLayout继承自UICollectionViewLayout,主要用来表现UICollectionView的布局以及一些属性。

4.实现- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath;

-(UICollectionViewCell*)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
UICollectionViewCell*cell=[collectionView dequeueReusableCellWithReuseIdentifier:CELL_STR forIndexPath:indexPath];
for (UIView*view in cell.contentView.subviews) {
if (view) {
[view removeFromSuperview];
}
}
UIImageView*imgView=[[UIImageView alloc] initWithFrame:CGRectMake(0, 0, ITEM_WIDTH, ITEM_WIDTH)];
if (indexPath.row>4) {
imgView.image=[UIImage imageNamed:@"apple.png"];
}else{
imgView.image=[UIImage imageNamed:@"applec.png"];
}
[cell.contentView addSubview:imgView];
[imgView release];
return cell;
}

5.cell的大小

-(CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath{
return CGSizeMake(130,130);
}

 

CircleLayout的代码:

CircleLayout.h

#import
#import
@interface CircleLayout : UICollectionViewLayout
@property (nonatomic, assign) CGPoint center;
@property (nonatomic, assign) CGFloat radius;
@property (nonatomic, assign) NSInteger cellCount;
@end

 

CircleLayout.m

 

#define ITEM_SIZE 130
#import "CircleLayout.h"

@implementation CircleLayout
@synthesize cellCount,center,radius;

  • (void)prepareLayout{
    [super prepareLayout];
    CGSize size = self.collectionView.frame.size;
    cellCount=[self.collectionView numberOfItemsInSection:0];
    center=CGPointMake(size.width/2, size.height/2);
    radius = MIN(size.width, size.height) / 2.5;
    }
    -(CGSize)collectionViewContentSize{
    return [self collectionView].frame.size;
    }
  • (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)path
    {
    UICollectionViewLayoutAttributes* attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:path];
    attributes.size = CGSizeMake(ITEM_SIZE, ITEM_SIZE);
    attributes.center = CGPointMake(center.x radius * cosf(2 * path.item * M_PI /cellCount),

center.y radius * sinf(2 * path.item * M_PI /cellCount));
return attributes;
}
-(NSArray*)layoutAttributesForElementsInRect:(CGRect)rect{
NSMutableArray* attributes = [NSMutableArray array];
for (NSInteger i=0 ; i < self.cellCount; i ) {
NSIndexPath* indexPath = [NSIndexPath indexPathForItem:i inSection:0];
[attributes addObject:[self layoutAttributesForItemAtIndexPath:indexPath]];
}
return attributes;
}

  • (UICollectionViewLayoutAttributes *)initialLayoutAttributesForInsertedItemAtIndexPath:(NSIndexPath *)itemIndexPath{
    UICollectionViewLayoutAttributes* attributes = [self layoutAttributesForItemAtIndexPath:itemIndexPath];
    attributes.alpha = 0.0;
    attributes.center = CGPointMake(center.x, center.y);
    return attributes;
    }
  • (UICollectionViewLayoutAttributes *)finalLayoutAttributesForDeletedItemAtIndexPath:(NSIndexPath *)itemIndexPath
    {
    UICollectionViewLayoutAttributes* attributes = [self layoutAttributesForItemAtIndexPath:itemIndexPath];
    attributes.alpha = 0.0;
    attributes.center = CGPointMake(center.x, center.y);
    attributes.transform3D = CATransform3DMakeScale(0.1, 0.1, 1.0);
    return attributes;
    }
    @end

其代理方法和属性模仿UICollectionViewFlowLayout 所写,使用方法和UICollectionViewFlowLayout类似

通过自定义collectionViewCell和重写collectionViewLayout

2 。先列举一个简单的例子

 

UICollectionView的使用 UICollectionView的使用有两种方法,一种是继承UICollectionViewController,这个Controller会自带一个UICollectionView;另外一种是...

功能描述:

一.自定义UICollectionViewCell

图片 1

1 > 满足UICollectionViewFlowLayout提供的普通的线性布局和网格布局

如>#import@interfaceCollectionCell : UICollectionViewCell

效果图1.png

2> 满足单区和多区的瀑布流布局。

@property (strong, nonatomic)  UIImageView*imageView;

我们可以看到这个流式布局,我们可以自定义 布局,继承与UICollectionViewFlowLayout

3> 满足多区瀑布流时每个区的列数可以不同

@property (strong, nonatomic)  UIImageView*bottomBar;

我现在将 主要代码 贴出来 ,希望对正在使用UICollectionView的同学有所帮助

4> 满足设置header和footer

@property (strong, nonatomic) CBAutoScrollLabel*productNameLbl;

自定义 布局,继承与 UICollectionViewFlowLayout
CoustomFlowLayOut.m

5> 满足设置header和footer的间距

@property (strong, nonatomic) UILabel*priceLbl;@end////CollectionCell.m//CollectionView////Created by Piosa on 14-6-13.//Copyright (c) 2014年 D2space. All rights reserved.//#import"CollectionCell.h"@implementationCollectionCell- (id)initWithFrame:(CGRect)frame{self=[super initWithFrame:frame];if(self){self.imageView=[[UIImageView alloc]init];[selfaddSubview:self.imageView];//--------------//透明栏//--------------floath=30;floatx=0;floatw=CGRectGetWidth(frame);floaty=0;self.bottomBar=[[UIImageView alloc]initWithFrame:CGRectMake(x, y, w, h)];[selfaddSubview:self.bottomBar];self.bottomBar.image=[UIImage imageNamed:@"toumingse.png"];//产品名y=0;floattempH=h/2;x=3;self.productNameLbl=[[CBAutoScrollLabel alloc]initWithFrame:CGRectMake(x, y, w, tempH)];self.productNameLbl.backgroundColor=[UIColor clearColor];[self.bottomBar addSubview:self.productNameLbl];//产品价格y =tempH;self.priceLbl=[[UILabel alloc]initWithFrame:CGRectMake(x, y, w, tempH)];self.priceLbl.textColor=[UIColor whiteColor];self.priceLbl.backgroundColor=[UIColor clearColor];self. priceLbl.font=[UIFont systemFontOfSize:12];[self.bottomBar addSubview:self.priceLbl];}returnself;}@end二.创建自定义布局#import#pragmamark WaterF@protocolWaterFLayoutDelegate @required- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath;

  • (instancetype)init
    {
    if (self = [super init]) {
    }
    return self;
    }

注意:本文不涉及到装饰视图的相关代理方法以及计算。

@optional- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout heightForHeaderInSection:(NSInteger)section;- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout heightForFooterInSection:(NSInteger)section;@end@interfaceMyLayout : UICollectionViewLayout{floatx;floatleftY;floatrightY;

/**

首先要明白的事情:collectionView与collocationViewLayout的关系。

}

  • 当collectionView的显示范围发生改变的时候,是否需要重新刷新布局
  • 一旦重新刷新布局,就会重新调用下面的方法:
    1.prepareLayout
    2.layoutAttributesForElementsInRect:方法
    */

collocationView负责展示,collectionviewLayout负责提供如何展示,包括cell的大小位置,header和footer的大小位置等,UICollectionViewFlowLayout 继承自UICollectionViewLayout是苹果公司封装好的layout,可以实现简单的网格和线性布局,当cell的大小和间距一样时可以用UICollectionViewFlowLayout,如果要实现比较复杂的布局,就需要自定义了。

@propertyfloatitemWidth;@property (nonatomic, assign) CGPoint center;@property (nonatomic, assign) CGFloat radius;@property (nonatomic, assign) NSInteger cellCount;///The delegate will point to collection view's delegate automatically.@property (nonatomic, weak)iddelegate;///Array to store attributes for all items includes headers, cells, and footers@property (nonatomic, strong) NSMutableArray*allItemAttributes;@property (nonatomic, assign) UIEdgeInsets sectionInset;@end#import"MyLayout.h"#defineITEM_SIZE 70@implementationMyLayout-(void)prepareLayout{[super prepareLayout];self.itemWidth=150;self.sectionInset=UIEdgeInsetsMake(5,5,5,5);self.delegate= (id )self.collectionView.delegate;CGSize size=self.collectionView.frame.size;_cellCount= [[selfcollectionView] numberOfItemsInSection:0];_center= CGPointMake(size.width /2.0, size.height /2.0);_radius= MIN(size.width, size.height) /2.5;

其次,要了解UICollectionViewLayoutAttributes 类的属性,以下是每一个cell的属性,都是通过UICollectionViewLayoutAttributes属性体现出来的。

}-(CGSize)collectionViewContentSize{returnCGSizeMake(320, (leftY>rightY?leftY:rightY));

  • (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
    {
    return YES;
    }

CGRect frame; // cell的大小已经x,y值

}- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath  withIndex:(int)index{CGSize itemSize= [self.delegatecollectionView:self.collectionView layout:selfsizeForItemAtIndexPath:indexPath];CGFloat itemHeight= floorf(itemSize.height *self.itemWidth /itemSize.width);UICollectionViewLayoutAttributes*attributes =[UICollectionViewLayoutAttributeslayoutAttributesForCellWithIndexPath:indexPath];index =1;if(index%2==0){x =(self.itemWidth self.sectionInset.left);rightY =self.sectionInset.top;attributes.frame=CGRectMake(x, rightY,self.itemWidth, itemHeight);rightY =itemHeight;}else{x=self.sectionInset.left;leftY =self.sectionInset.top;attributes.frame=CGRectMake(x, leftY,self.itemWidth, itemHeight);leftY =itemHeight;}returnattributes;

/**

CGPoint center;//cell的中心点

}-(NSArray*)layoutAttributesForElementsInRect:(CGRect)rect{x=0;leftY=0;rightY=0;NSMutableArray* attributes =[NSMutableArrayarray];NSLog(@"cellCount=%d",self.cellCount);for(NSInteger i=0; i

  • 用来做布局的初始化操作(不建议在init方法中进行布局的初始化操作)
    */

CGSize size;// cell的size

}- (UICollectionViewLayoutAttributes *)initialLayoutAttributesForInsertedItemAtIndexPath:(NSIndexPath*)itemIndexPath{UICollectionViewLayoutAttributes* attributes =[selflayoutAttributesForItemAtIndexPath:itemIndexPath];attributes.alpha=0.0;attributes.center=CGPointMake(_center.x, _center.y);returnattributes;

CATransform3D transform3D;// cell的3D旋转

}- (UICollectionViewLayoutAttributes *)finalLayoutAttributesForDeletedItemAtIndexPath:(NSIndexPath*)itemIndexPath{UICollectionViewLayoutAttributes* attributes =[selflayoutAttributesForItemAtIndexPath:itemIndexPath];attributes.alpha=0.0;attributes.center=CGPointMake(_center.x, _center.y);attributes.transform3D= CATransform3DMakeScale(0.1,0.1,1.0);returnattributes;

  • (void)prepareLayout
    {
    [super prepareLayout];

    // 水平滚动
    self.scrollDirection = UICollectionViewScrollDirectionHorizontal;
    // 设置内边距
    CGFloat inset = (self.collectionView.frame.size.width - self.itemSize.width) * 0.5;
    self.sectionInset = UIEdgeInsetsMake(0, inset, 0, inset);
    }

CGRect bounds NS_AVAILABLE_IOS;

}@end三.创建UICollectionView用之前自定义的布局进行初始化并注册之前自定义的UICollectionViewCell,参照如下1.创建变量

/**
UICollectionViewLayoutAttributes attrs;
1.一个cell对应一个UICollectionViewLayoutAttributes对象
2.UICollectionViewLayoutAttributes对象决定了cell的frame
/
/**

CGAffineTransform transform NS_AVAILABLE_IOS; // cell 的旋转

@property (strong, nonatomic)  UICollectionView*collectionView;2.初始化

  • 这个方法的返回值是一个数组(数组里面存放着rect范围内所有元素的布局属性)
  • 这个方法的返回值决定了rect范围内所有元素的排布(frame)
    */

CGFloat alpha;//alp值

MyLayout*layout=[[MyLayout alloc]init];collectionView=[[UICollectionView alloc]initWithFrame:CGRectMake(0,0, CGRectGetWidth(self.frame),CGRectGetHeight(self.frame)) collectionViewLayout:layout];collectionView.delegate=self;collectionView.dataSource=self;collectionView.scrollEnabled=YES;[selfaddSubview:collectionView];collectionView.backgroundColor=[UIColor clearColor];[collectionView registerClass:[CollectionCellclass] forCellWithReuseIdentifier:@"CollectionCell"];3.实现代理#pragma-mark UICollectionView delegate//根据传入的图片得到宽高-(CGSize)getImgSize:(UIImage *)image{//得到比例floatrate=(itemWidth/image.size.width);returnCGSizeMake(itemWidth, (image.size.height*rate));

NSInteger zIndex; // default is 0 //z轴

}//定义每个UICollectionView 的大小- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath{NSDictionary*item=productList[indexPath.row];return[selfgetImgSize:[item objectForKey:KEY_PRODUCT_IMG]];}//定义每个UICollectionView 的 margin-(UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section{returnUIEdgeInsetsMake(5,5,5,5);

  • (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
    {
    // 获得super已经计算好的布局属性
    NSArray *array = [super layoutAttributesForElementsInRect:rect] ;

    // 计算collectionView最中心点的x值
    CGFloat centerX = self.collectionView.contentOffset.x self.collectionView.frame.size.width * 0.5;

    // 在原有布局属性的基础上,进行微调
    for (UICollectionViewLayoutAttributes *attrs in array) {
    // cell的中心点x 和 collectionView最中心点的x值 的间距
    CGFloat delta = ABS(attrs.center.x - centerX);

      // 根据间距值 计算 cell的缩放比例
      CGFloat scale = 1 - delta / self.collectionView.frame.size.width;
    
      // 设置缩放比例
      attrs.transform = CGAffineTransformMakeScale(scale, scale);
    

    }
    return array;
    }

getter=isHidden) BOOL hidden; // As an optimization,

}-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{NSLog(@"indexPath=%d",indexPath.row);NSDictionary*item=productList[indexPath.row];DetailViewController*detailView=[[DetailViewController alloc]init];detailView.productID=[[item objectForKey:PRODUCT_ID] integerValue];[viewController.navigationController pushViewController:detailView animated:YES]; }//每个section的item个数-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{returnproductList.count;

/**

还有,要理解UICollectionViewLayout的几个方法:

}-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionViews cellForItemAtIndexPath:(NSIndexPath*)indexPath{CollectionCell*cell = (CollectionCell *)[collectionViewsdequeueReusableCellWithReuseIdentifier:@"CollectionCell"forIndexPath:indexPath];cell.backgroundColor=[UIColor clearColor];//图片名称//NSString *imageToLoad = [NSString stringWithFormat:@"%d.png", indexPath.row];NSDictionary*item=productList[indexPath.row];NSString*productName;NSString*productImgUrl;if([dataTypeps isEqualToString:TYPE_HISTORY]){NSArray*temp=[[item objectForKey:PRODUCT_NAME] componentsSeparatedByString:@":"];productName=temp[0];}else{productName=[item objectForKey:PRODUCT_NAME];}UIImage*img=[item objectForKey:KEY_PRODUCT_IMG];CGSize size=[selfgetImgSize:img];//加载图片cell.imageView.image=img;cell.imageView.frame=CGRectMake(0,0, size.width, size.height);//--------------//透明栏//--------------floath=30;floatx=0;floatw=size.width;floaty=size.height-h;cell.bottomBar.frame=CGRectMake(x, y, w, h);cell.bottomBar.backgroundColor=[UIColor clearColor];//产品名y=0;floattempH=h/2;x=3;cell.productNameLbl.frame=CGRectMake(x, y, w, tempH);cell.productNameLbl.backgroundColor=[UIColor clearColor];[commonUtil setScrollLabel:cell.productNameLbl withText:productName withCenter:UITextAlignmentLeftwithFontSize:14withTextColor:[UIColor whiteColor]];//产品价格y =tempH;cell.priceLbl.frame=CGRectMake(x, y, w, tempH);cell.priceLbl.text=[NSString stringWithFormat:@"¥%@",[item objectForKey:PRODUCT_PRICE]];returncell;

  • 这个方法的返回值,就决定了collectionView停止滚动时的偏移量

1, prepareLayout :是专门用来准备布局的,在prepareLayout方法里面我们可以事先就计算后面要用到的布局信息并存储起来,防止后面方法多次计算,提高性能。例如,我们可以在此方法就计算好每个cell的属性、整个CollectionView的内容尺寸等等。此方法在布局之前会调用一次,之后只有在调用invalidateLayout、shouldInvalidateLayoutForBoundsChange:返回YES和UICollectionView刷新的时候才会调用。

}

*/

2,- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath 返回对应的indexPath的cell的attributes

  • (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity
    {
    // 计算出最终显示的矩形框
    CGRect rect;
    rect.origin.y = 0;
    rect.origin.x = proposedContentOffset.x;
    rect.size = self.collectionView.frame.size;

    // 获得super已经计算好的布局属性
    NSArray *array = [super layoutAttributesForElementsInRect:rect];

    // 计算collectionView最中心点的x值
    CGFloat centerX = proposedContentOffset.x self.collectionView.frame.size.width * 0.5;

    // 存放最小的间距值
    CGFloat minDelta = MAXFLOAT;
    for (UICollectionViewLayoutAttributes *attrs in array) {
    if (ABS(minDelta) > ABS(attrs.center.x - centerX)) {
    minDelta = attrs.center.x - centerX;
    }
    }

    // 修改原有的偏移量
    proposedContentOffset.x = minDelta;
    return proposedContentOffset;
    }

3- (UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath 返回对应的header和footer的attributes

自定义cell的话,就是只有一张图片,这里不再赘述,控制器里
mainViewController.m

4 - collectionViewContentSize ;collectionView的size 这个size不是可视范围的size是整个collectionView的size

  • (void)viewDidLoad {
    [super viewDidLoad];

    // 创建布局
    CoustomFlowLayOut *layout = [[CoustomFlowLayOut alloc] init];
    layout.itemSize = CGSizeMake(100, 100);

    // 创建CollectionView
    CGFloat collectionW = self.view.frame.size.width;
    CGFloat collectionH = 200;
    CGRect frame = CGRectMake(0, 150, collectionW, collectionH);
    UICollectionView *collectionView = [[UICollectionView alloc] initWithFrame:frame collectionViewLayout:layout];
    collectionView.dataSource = self;
    collectionView.delegate = self;
    [self.view addSubview:collectionView];

    // 注册
    [collectionView registerNib:[UINib nibWithNibName:NSStringFromClass([CYPhotoCell class]) bundle:nil] forCellWithReuseIdentifier:CYPhotoId];
    }

5 - (NSArray *)layoutAttributesForElementsInRect:rect 返回在rect范围内所有cell footer和head的attribute

pragma mark - <UICollectionViewDataSource>

  • (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
    {
    return 20;
    }

  • (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
    {
    CYPhotoCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:CYPhotoId forIndexPath:indexPath];

    cell.imageName = [NSString stringWithFormat:@"%zd", indexPath.item 1];

    return cell;
    }

了解以上的几点就可以开始计算了。计算的顺序是从上到下,即从区头到每个区的cell再到区尾

pragma mark - <UICollectionViewDelegate>

  • (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
    {
    NSLog(@"------%zd", indexPath.item);
    }
    @end

上边就是 主要的代码,就可以很简单的实现 上边图片的功能

大家还可能 见过商城中的 商品中的布局 ,类似是这样的,

图片 2

效果图.png

其实上边的这种效果也很容易实现,只要自己 自定义布局就行了,只是这个布局比 上边那种布局更复杂一点,但是整体的思路还是相同的,就是 自定义 布局,自定义cell,然后控制器 来利用使用它们,在这里 我将主要的代码贴出来,让大家参考,希望对大家 深入了解认识UICollectionView有好的帮助
同样的我先贴出 自定义layOut的 代码
.h中

设置一些数组用于存储计算好的值:如下

import <UIKit/UIKit.h>

@class MKMasonryViewLayout;

@protocol MKMasonryViewLayoutDelegate <NSObject>
@required

  • (CGFloat) collectionView:(UICollectionView) collectionView
    layout:(MKMasonryViewLayout
    ) layout
    heightForItemAtIndexPath:(NSIndexPath*) indexPath;
    @end

@interface MKMasonryViewLayout : UICollectionViewLayout
@property (nonatomic, assign) NSUInteger numberOfColumns;
@property (nonatomic, assign) CGFloat interItemSpacing;
@property (weak, nonatomic) IBOutlet id<MKMasonryViewLayoutDelegate> delegate;
@end

.m中

//存放attribute的数组

import "MKMasonryViewLayout.h"

@interface MKMasonryViewLayout (/Private Methods/)
@property (nonatomic, strong) NSMutableDictionary *lastYValueForColumn;
@property (strong, nonatomic) NSMutableDictionary *layoutInfo;
@end

@implementation MKMasonryViewLayout

-(void) prepareLayout {

//集合试图 布局 前 会调用这个方法,在这里将 布局物件的边框都计算好 缓存到某个地方

self.numberOfColumns = 3;
self.interItemSpacing = 12.5;

CGFloat currentColumn = 0;
CGFloat fullWidth = self.collectionView.frame.size.width;
CGFloat availableSpaceExcludingPadding = fullWidth - (self.interItemSpacing * (self.numberOfColumns 1));
CGFloat itemWidth = availableSpaceExcludingPadding / self.numberOfColumns;//每个物件的宽度

self.lastYValueForColumn = [NSMutableDictionary dictionary];

self.layoutInfo = [NSMutableDictionary dictionary];
NSIndexPath *indexPath;
NSInteger numSections = [self.collectionView numberOfSections];

for(NSInteger section = 0; section < numSections; section ) {
//collectionView 是 横着 排列的,而不是 竖着排列的
NSInteger numItems = [self.collectionView numberOfItemsInSection:section];
//每个 section 上含有几个 nuMItems
for(NSInteger item = 0; item < numItems; item ){
indexPath = [NSIndexPath indexPathForItem:item inSection:section];

  UICollectionViewLayoutAttributes *itemAttributes =
  [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];

  CGFloat x = self.interItemSpacing   (self.interItemSpacing   itemWidth) * currentColumn;
  CGFloat y = [self.lastYValueForColumn[@(currentColumn)] doubleValue];

  CGFloat height = [((id<MKMasonryViewLayoutDelegate>)self.collectionView.delegate)
                    collectionView:self.collectionView
                    layout:self
                    heightForItemAtIndexPath:indexPath];

  itemAttributes.frame = CGRectMake(x, y, itemWidth, height);
  y = height;
  y  = self.interItemSpacing;

  self.lastYValueForColumn[@(currentColumn)] = @(y);

  currentColumn   ;
  if(currentColumn == self.numberOfColumns) currentColumn = 0;
  self.layoutInfo[indexPath] = itemAttributes;
}

}
}

  • (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {

    NSMutableArray *allAttributes = [NSMutableArray arrayWithCapacity:self.layoutInfo.count];

    [self.layoutInfo enumerateKeysAndObjectsUsingBlock:^(NSIndexPath *indexPath,
    UICollectionViewLayoutAttributes *attributes,
    BOOL *stop) {

    if (CGRectIntersectsRect(rect, attributes.frame)) {
    [allAttributes addObject:attributes];
    }
    }];
    return allAttributes;
    }

-(CGSize) collectionViewContentSize {

NSUInteger currentColumn = 0;
CGFloat maxHeight = 0;
do {
CGFloat height = [self.lastYValueForColumn[@(currentColumn)] doubleValue];
if(height > maxHeight)
maxHeight = height;
currentColumn ;
} while (currentColumn < self.numberOfColumns);

return CGSizeMake(self.collectionView.frame.size.width, maxHeight);
}

@end

上边就是 完整的把想要的布局 给 布置好了,同样自定义cell这里也不 多谢,现在把 控制器的代码贴出来!

@property (nonatomic, strong) NSMutableArray *attrsArray;

import "SCTViewController.h"

@interface SCTViewController ()<UICollectionViewDataSource, UICollectionViewDelegate, MKMasonryViewLayoutDelegate, UICollectionViewDelegateFlowLayout>

@end

@implementation SCTViewController

  • (void)viewDidLoad
    {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    }

  • (void)didReceiveMemoryWarning
    {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
    }

  • (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {

    return 1;
    }

  • (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {

    return 40;
    }

  • (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {

    static NSString *CellIdentifier = @"Cell";
    UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:CellIdentifier
    forIndexPath:indexPath];
    cell.backgroundColor = [UIColor redColor];
    return cell;
    }

// this will be called if our layout is UICollectionViewFlowLayout

  • (CGSize)collectionView:(UICollectionView )collectionView layout:(UICollectionViewLayout)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {

    CGFloat randomHeight = 100 (arc4random() % 140);
    return CGSizeMake(100, randomHeight); // 100 to 240 pixels tall
    }

// this will be called if our layout is MKMasonryViewLayout

  • (CGFloat) collectionView:(UICollectionView) collectionView
    layout:(MKMasonryViewLayout
    ) layout
    heightForItemAtIndexPath:(NSIndexPath*) indexPath {

    // we will use a random height from 100 - 400

    CGFloat randomHeight = 100 (arc4random() % 140);
    return randomHeight;
    }

@end

上边的代码就可以实现上图中所要实现的功能,希望可以帮到个位!希望个位提出宝贵意见!

//存放当前区中各个列的当前的高度

@property (nonatomic, strong) NSMutableArray *columnHeights;

//collectionView的Content的高度

@property (nonatomic, assign) CGFloat contentHeight;

//记录每个区最高的

@property (nonatomic, assign) CGFloat lastContentHeight;

//每个区的区头和上个区的区尾的距离

@property (nonatomic, assign) CGFloat spacingWithLastSection;

首先是重写 prepareLayout方法,也是最重要的一步。在此方法中完成初始化。所有的计算都置为零。

第一步:通过 [self.collectionView numberOfSections] 方法获取collectionView中一共有几个区。设置一个for循环。

第二步:通过 - (UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath 获取每个header的属性。计算完成之后把attributes添加到attrsArray 数组中 ,同时还有根据sectionInsets 等参数改变contentHeight 的高度

第三步: 设置区头完成之后,在循环中根据 [self.collectionView numberOfItemsInSection:i] 获取相应的区有多少个cell

在这一步中计算是最麻烦的。可以分为如下步骤:

1> 计算每一个cell的frame。 根据此区中一共有几列和屏幕的宽度,以及每个cell的之间的间距,计算出每个cell的宽度,因为高度是外面传过来的,所以高度不需要计算 。那么还需要知道cell的x值和y值。

x值:首先取出当前区的中哪一列最低。

NSInteger tempMinColumn = 0; //默认第 0 列最小

CGFloat minColumnHeight = [self.columnHeights[0] doubleValue]; // 取出最小的那一列的高度

for (NSInteger i = 0; i < self.columnCount; i ) {

CGFloat columnH = [self.columnHeights[i] doubleValue];

if (minColumnHeight > columnH) {

minColumnHeight = columnH;

tempMinColumn = i;

} else {}

}

tempMinColumn 就是最小的那一列

x值就可以根据sectionInsets , 每个cell的左右间距,和cell的宽度算出 CGFloat cellX = self.sectionInsets.left tempMinColumn * (cellWeight self.interitemSpacing);

y值:上面已经求出高度最小的那一列,以及最小的那一列的高度。

y值就 cellY = minColumnHeight

注意://如果cell的y值不等于上个区的最高的高度 即不是此区的第一列 要加上此区的每个cell的上下间距

if (cellY != self.lastContentHeight) {

cellY = self.lineSpacing;

} else {}

这样就可以知道了 cell的frame了, 即attributes.frame = CGRectMake(cellX, cellY, cellWeight, cellHeight);

2> 要更新 contentHeight (当前collectionView的内容的高度) 和columnHeights(当区的每列的高度或者说每列的最后一个cell的y值 height)

那么这样相应cell的值就计算完毕 ,在此函数返回值处添加到attrsArray 中去。

第四部:同header的计算方式一样 在- (UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath 计算footer的frame

一共多少个区 ,每个区的header的frame是多少,每个区中有多少个cell 每个cell的frame是多少 ,每个区的footer的frame是多少,以此循环计算出所有的attributes,在- (NSArray *)layoutAttributesForElementsInRect:rect 返回计算的attributes

注意 :在计算每个attributes时 collectionView的内容的高度即contentHeight collectionView上个区的最高的那一列的高度即lastContentHeight 都在改变。

在- collectionViewContentSize {

return CGSizeMake(self.collectionView.frame.size.width, self.contentHeight);

} 中返回collectionView ContentSize 完成布局。

为了支持扩展性和易用性,我完全模仿 UICollectionViewFlowLayout 的用法设置代理方法和属性。至于其使用方法,和UICollectionViewFlowLayout 一样的。代理方法和属性如下。

@property (nonatomic, weak) id delegate;

// 区的sectionInsets

@property (nonatomic,assign) UIEdgeInsets sectionInsets;

//每个区的列数

@property (nonatomic,assign) NSInteger columnCount;

// 每个cell的上下间距

@property (nonatomic,assign) CGFloat lineSpacing;

//每个cell的左右间距

@property (nonatomic,assign) CGFloat interitemSpacing;

//header的size

@property (nonatomic,assign) CGSize headerReferenceSize;

// footer的size

@property (nonatomic,assign) CGSize footerReferenceSize;

上述的这些参数 如果每个区都一样,则可以在layout初始化的时候设置,如过每个区的参数设置都不一样,比如第一个区是两列,第二个区是一列,不用担心,用代理。

代理方法支持分区设置这些参数。

@protocol JWCCustomLayoutDelegate

@required

// cell 高

- collectionView:(UICollectionView *)collectionView layout:(JWCCustomLayout *)collectionViewLayout heightForRowAtIndexPath:(NSIndexPath *)indexPath itemWidth:itemWidth ;

@optional

// headersize

- collectionView:(UICollectionView *)collectionView layout:(JWCCustomLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section;

// footer 的 size

- collectionView:(UICollectionView *)collectionView layout:(JWCCustomLayout *)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section;

// 每个区的边距

- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(JWCCustomLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section;

// 每个区多少列

- (NSInteger)collectionView:(UICollectionView *)collectionView layout:(JWCCustomLayout *)collectionViewLayout columnNumberAtSection:(NSInteger )section;

// 每个区多少中行距

- (NSInteger)collectionView:(UICollectionView *)collectionView layout:(JWCCustomLayout *)collectionViewLayout lineSpacingForSectionAtIndex:(NSInteger)section;

// 每个 item 之间的左右间距

- collectionView:(UICollectionView *)collectionView layout:(JWCCustomLayout*)collectionViewLayout interitemSpacingForSectionAtIndex:(NSInteger)section;

// 本区区头和上个区区尾的间距

- collectionView:(UICollectionView *)collectionView layout:(JWCCustomLayout*)collectionViewLayout spacingWithLastSectionForSectionAtIndex:(NSInteger)section; (注意:在collectionViewFolwLayout中是无法设置当前的区头和上个区尾的间距的,为了弥补这一缺憾,特此添加这个方法)

以上只是大致计算步骤,具体实现代码见demo

转载请注明出处 掘金老 JI 谢谢

看完如果对你有用 请点赞鼓励下中不中?

本文由星彩网app下载发布于计算机编程,转载请注明出处:自定义collocationViewLayout完结瀑布流,的灵活利用

TAG标签: 星彩网app下载
Ctrl+D 将本页面保存为书签,全面了解最新资讯,方便快捷。