博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
iOS-UICollectionView
阅读量:6441 次
发布时间:2019-06-23

本文共 33500 字,大约阅读时间需要 111 分钟。

1------------------------------------------------------------------------------------------------------------------------

本章通过先总体介绍UICollectionView及其常用方法,再结合一个实例,了解如何使用UICollectionView。

 

UICollectionView 和 UICollectionViewController 类是iOS6 新引进的API,用于展示集合视图,布局更加灵活,可实现多列布局,用法类似于UITableView 和 UITableViewController 类。

使用UICollectionView 必须实现UICollectionViewDataSource,UICollectionViewDelegate,UICollectionViewDelegateFlowLayout这三个协议。

 

下面先给出常用到的一些方法。(只给出常用的,其他的可以查看相关API) 

  1. #pragma mark -- UICollectionViewDataSource   
  1. //定义展示的UICollectionViewCell的个数  
  2. -(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section  
  3. {  
  4.     return 30;  
  5. }   
  1. //定义展示的Section的个数  
  2. -(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView  
  3. {  
  4.     return 1;  
  5. }   
  1. //每个UICollectionView展示的内容  
  2. -(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath  
  3. {  
  4.     static NSString * CellIdentifier = @"GradientCell";  
  5.     UICollectionViewCell * cell = [collectionView dequeueReusableCellWithReuseIdentifier:CellIdentifier forIndexPath:indexPath];  
  6.   
  7.     cell.backgroundColor = [UIColor colorWithRed:((10 * indexPath.row) / 255.0) green:((20 * indexPath.row)/255.0) blue:((30 * indexPath.row)/255.0) alpha:1.0f];  
  8.     return cell;  
  9. }   
  1. #pragma mark --UICollectionViewDelegateFlowLayout   
  1. //定义每个UICollectionView 的大小  
  2. - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath  
  3. {  
  4.     return CGSizeMake(96, 100);  
  5. }   
  1. //定义每个UICollectionView 的 margin  
  2. -(UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section  
  3. {  
  4.     return UIEdgeInsetsMake(5, 5, 5, 5);  
  5. }   
  1. #pragma mark --UICollectionViewDelegate   
  1. //UICollectionView被选中时调用的方法  
  2. -(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath  
  3. {  
  4.     UICollectionViewCell * cell = (UICollectionViewCell *)[collectionView cellForItemAtIndexPath:indexPath];  
  5.     cell.backgroundColor = [UIColor whiteColor];  
  6. }   
  1. //返回这个UICollectionView是否可以被选择  
  2. -(BOOL)collectionView:(UICollectionView *)collectionView shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath  
  3. {  
  4.     return YES;  
  5. }  

 

 

 

下面通过一个例子具体介绍下。(例子来自网络。但是是通过第三方获得的,无法取得链接。还望见谅。)

 

iOS CollectionView的出现是一大福利,再也不用用TableView来定义复杂的多栏表格了,用法与Table类似,只是Cell必须自己添加,无默认模式

由于CollectionView没有默认的Cell布局,所以一般还是自定义方便又快捷

一、自定义Cell

1、新建类CollectionCell继承自UICollectionViewCell

2、新建Xib,命名为CollectionCell.xib

a.选中CollectionCell.xib删掉默认的View,从控件中拖一个Collection View Cell(图3)到画布中,设置大小为95*116;

 

b.选中刚刚添加的Cell,更改类名为CollectionCell,如图4

c.在CollectionCell.xib的CollectionCell中添加一个ImageView和一个Label(图5)

d.创建映射, 图6,图7

e.选中CollectionCell.m , 重写init方法 

  1. - (id)initWithFrame:(CGRect)frame  
  2. {  
  3.     self = [super initWithFrame:frame];  
  4.     if (self)  
  5.     {  
  6.         // 初始化时加载collectionCell.xib文件  
  7.         NSArray *arrayOfViews = [[NSBundle mainBundle] loadNibNamed:@"CollectionCell" owner:self options:nil];  
  8.           
  9.         // 如果路径不存在,return nil  
  10.         if (arrayOfViews.count < 1)  
  11.         {  
  12.             return nil;  
  13.         }  
  14.         // 如果xib中view不属于UICollectionViewCell类,return nil  
  15.         if (![[arrayOfViews objectAtIndex:0] isKindOfClass:[UICollectionViewCell class]])  
  16.         {  
  17.             return nil;  
  18.         }  
  19.         // 加载nib  
  20.         self = [arrayOfViews objectAtIndex:0];  
  21.     }  
  22.     return self;  
  23. }  

f.选中CollectionCell.xib 修改其identifier为CollectionCell。

二、定义UICollectionView;

1、拖动一个Collection View到指定ViewController的View上

2、连线dataSource和delegate,并创建映射,命名为CollectionView

3、选中CollectionView的标尺,将Cell Size的Width和Height改成与自定义的Cell一样的95*116,图8

    

4、选中CollectionView的属性,可以修改其属性,比如是垂直滑动,还是水平滑动,选择Vertical或Horizontal

5、选中CollectionViewCell,修改Class,继承自CollectionCell

5、在ViewDidLoad方法中声明Cell的类,在ViewDidLoad方法中添加,此句不声明,将无法加载,程序崩溃

其中,CollectionCell是这个Cell的标识(之前几步已经定义过了。 ) 

  1. [self.collectionView registerClass:[CollectionCell class] forCellWithReuseIdentifier:@"CollectionCell"];  

6、在ViewController.h中声明代理 

  1. @interface ViewController : UIViewController<UICollectionViewDataSource,UICollectionViewDelegate>  

 

7、在.m文件中实现代理方法 

  1. //每个section的item个数  
  2. -(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section  
  3. {  
  4.     return 12;  
  5. }  
  6.   
  7. -(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath  
  8. {  
  9.       
  10.     CollectionCell *cell = (CollectionCell *)[collectionView dequeueReusableCellWithReuseIdentifier:@"CollectionCell" forIndexPath:indexPath];  
  11.       
  12.     //图片名称  
  13.     NSString *imageToLoad = [NSString stringWithFormat:@"%d.png", indexPath.row];  
  14.     //加载图片  
  15.     cell.imageView.image = [UIImage imageNamed:imageToLoad];  
  16.     //设置label文字  
  17.     cell.label.text = [NSString stringWithFormat:@"{%ld,%ld}",(long)indexPath.row,(long)indexPath.section];  
  18.       
  19.     return cell;  
  20. }  

 

8 。效果如图10

点击某项后跳转事件与UITableView类似,实现代理方法 

  1. -(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath  
------------------------------------------------------------------------------------------------------------------------------------
2

 

初始化部分:

UICollectionViewFlowLayout *flowLayout= [[UICollectionViewFlowLayout alloc]init];self.myCollectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(20, 20, 250, 350) collectionViewLayout:flowLayout];self.myCollectionView.backgroundColor = [UIColor grayColor];[self.myCollectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@“myCell"];self.myCollectionView.delegate = self;self.myCollectionView.dataSource = self;[self.view addSubview:self.myCollectionView];

 

UICollectionViewLayout

UICollectionViewLayout决定了UICollectionView如何显示在界面上,Apple提供了一个最简单的默认layout对象:UICollectionViewFlowLayout。

Flow Layout是一个Cells的线性布局方案,并具有页面和页脚。其可定制的内容如下:

itemSize属性

设定全局的Cell尺寸,如果想要单独定义某个Cell的尺寸,可以使用下面方法:

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

minimumLineSpacing属性

设定全局的行间距,如果想要设定指定区内Cell的最小行距,可以使用下面方法:

- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section

minimumInteritemSpacing属性

设定全局的Cell间距,如果想要设定指定区内Cell的最小间距,可以使用下面方法:

- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section;

scrollDirection属性

设定滚动方向,有UICollectionViewScrollDirectionVerticalUICollectionViewScrollDirectionHorizontal两个值。

headerReferenceSize属性与footerReferenceSize属性

设定页眉和页脚的全局尺寸,需要注意的是,根据滚动方向不同,header和footer的width和height中只有一个会起作用。如果要单独设置指定区内的页面和页脚尺寸,可以使用下面方法:

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section

sectionInset属性

设定全局的区内边距,如果想要设定指定区的内边距,可以使用下面方法:

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


 

然后需要实现三种类型的委托:UICollectionViewDataSource, UICollectionViewDelagate和UICollectionViewDelegateFlowLayout

@interface ViewController : UIViewController 

因为UICollectionViewDelegateFlowLayout实际上是UICollectionViewDelegate的一个子协议,它继承了UICollectionViewDelegate,所以只需要在声明处写上UICollectionViewDelegateFlowLayout就行了。


 

UICollectionViewDataSource

- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView

返回collection view里区(section)的个数,如果没有实现该方法,将默认返回1:

- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView{    return 2;}

 

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section

返回指定区(section)包含的数据源条目数(number of items),该方法必须实现:

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{    return 7;}

 

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath

返回某个indexPath对应的cell,该方法必须实现:

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{    UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"myCell" forIndexPath:indexPath];    if(indexPath.section==0)    {        cell.backgroundColor = [UIColor redColor];    }    else if(indexPath.section==1)    {        cell.backgroundColor = [UIColor greenColor];    }    return cell;}

UICollectionViewCell结构上相对比较简单,由下至上:

  • 首先是cell本身作为容器view
  • 然后是一个大小自动适应整个cell的backgroundView,用作cell平时的背景
  • 再其次是selectedBackgroundView,是cell被选中时的背景
  • 最后是一个contentView,自定义内容应被加在这个view

 

- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString*)kind atIndexPath:(NSIndexPath *)indexPath

为collection view添加一个补充视图(页眉或页脚)

 

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section

设定页眉的尺寸

 

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section

设定页脚的尺寸

 

- (void)registerClass:(Class)viewClass forSupplementaryViewOfKind:(NSString *)elementKind withReuseIdentifier:(NSString*)identifier

添加页眉和页脚以前需要注册类和标识:


 

添加补充视图的代码示例:

[self.myCollectionView registerClass:[MyHeadView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:@"hxwHeader"];[self.myCollectionView registerClass:[MyHeadView class] forSupplementaryViewOfKind:UICollectionElementKindSectionFooter withReuseIdentifier:@"hxwHeader"];-(CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section{    CGSize size = {240,25};    return size;}-(CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section{    CGSize size = {240,25};    return size;}- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath{    MyHeadView *headView;        if([kind isEqual:UICollectionElementKindSectionHeader])    {         headView = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:@"hxwHeader" forIndexPath:indexPath];        [headView setLabelText:[NSString stringWithFormat:@"section %d's header",indexPath.section]];    }    else if([kind isEqual:UICollectionElementKindSectionFooter])    {        headView = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionFooter withReuseIdentifier:@"hxwHeader" forIndexPath:indexPath];        [headView setLabelText:[NSString stringWithFormat:@"section %d's footer",indexPath.section]];    }    return headView;}

 

MyHeadView.h

#import 
@interface MyHeadView : UICollectionReusableView- (void) setLabelText:(NSString *)text;@end

 

MyHeadView.m

#import "MyHeadView.h"@interface MyHeadView()@property (strong, nonatomic) UILabel *label;@end@implementation MyHeadView- (id)initWithFrame:(CGRect)frame{    self = [super initWithFrame:frame];    if (self)    {        self.label = [[UILabel alloc] init];        self.label.font = [UIFont systemFontOfSize:18];        [self addSubview:self.label];    }    return self;}- (void) setLabelText:(NSString *)text{    self.label.text = text;    [self.label sizeToFit];}@end

 

在注册Cell和补充视图时,也可以用新建xib文件的方式:

[self.myCollectionView registerNib:[UINib nibWithNibName:@"MyCollectionCell" bundle:nil] forCellWithReuseIdentifier:@"hxwCell"];[self.myCollectionView registerNib:[UINib nibWithNibName:@"MySupplementaryView" bundle:nil] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:@"hxwHeader"];    [self.myCollectionView registerNib:[UINib nibWithNibName:@"MySupplementaryView" bundle:nil] forSupplementaryViewOfKind:UICollectionElementKindSectionFooter withReuseIdentifier:@"hxwFooter"];

用这种方式注册后,甚至可以不用新建类去绑定这个xib,直接通过viewWithTag的方式获取xib里的控件:

UICollectionReusableView *view =  [collectionView dequeueReusableSupplementaryViewOfKind :kind withReuseIdentifier:@"hxwHeader" forIndexPath:indexPath];UILabel *label = (UILabel *)[view viewWithTag:1];label.text = @"empty";

 


 

UICollectionViewDelegateFlowLayout

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

设定指定Cell的尺寸

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath{    if(indexPath.section==0 && indexPath.row==1)    {        return CGSizeMake(50, 50);    }    else    {        return CGSizeMake(75, 30);    }}

 

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

设定collectionView(指定区)的边距

- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section{    if(section==0)    {        return UIEdgeInsetsMake(35, 25, 15, 25);    }    else    {        return UIEdgeInsetsMake(15, 15, 15, 15);    }}

 

- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section

设定指定区内Cell的最小行距,也可以直接设置UICollectionViewFlowLayout的minimumLineSpacing属性

- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section{    if(section==0)    {        return 10.0;    }    else    {        return 20.0;    }}

 

- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section;

设定指定区内Cell的最小间距,也可以直接设置UICollectionViewFlowLayoutminimumInteritemSpacing属性

- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section{    if(section==0)    {        return 10.0;    }    else    {        return 20.0;    }}

 


UICollectionViewDelegate

- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath

当指定indexPath处的item被选择时触发

- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{[self.myArray removeObjectAtIndex:indexPath.row];[collectionView deleteItemsAtIndexPaths:[NSArray arrayWithObject:indexPath]];}

P.s. 当你删除或添加元素时,一定要更新numberOfItemsInSection的返回情况。

 

- (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath

当指定indexPath处的item被取消选择时触发,仅在允许多选时被调用

 

下面是三个和高亮有关的方法:

- (BOOL)collectionView:(UICollectionView *)collectionView shouldHighlightItemAtIndexPath:(NSIndexPath *)indexPath

- (void)collectionView:(UICollectionView *)collectionView didHighlightItemAtIndexPath:(NSIndexPath *)indexPath

- (void)collectionView:(UICollectionView *)collectionView didUnhighlightItemAtIndexPath:(NSIndexPath *)indexPath

 

事件的处理顺序如下:

  1. 手指按下
  2. shouldHighlightItemAtIndexPath (如果返回YES则向下执行,否则执行到这里为止)
  3. didHighlightItemAtIndexPath (高亮)
  4. 手指松开
  5. didUnhighlightItemAtIndexPath (取消高亮)
  6. shouldSelectItemAtIndexPath (如果返回YES则向下执行,否则执行到这里为止)
  7. didSelectItemAtIndexPath (执行选择事件)

如果只是简单实现点击后cell改变显示状态,只需要在cellForItemAtIndexPath方法里返回cell时,指定cell的selectedBackgroundView:

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{    UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"myCell" forIndexPath:indexPath];        UIView* selectedBGView = [[UIView alloc] initWithFrame:cell.bounds];    selectedBGView.backgroundColor = [UIColor blueColor];    cell.selectedBackgroundView = selectedBGView;        return cell;}

如果要实现点击时(手指未松开)的显示状态与点击后(手指松开)的显示状态,则需要通过上面提到的方法来实现:

- (BOOL)collectionView:(UICollectionView *)collectionView shouldHighlightItemAtIndexPath:(NSIndexPath *)indexPath{    return YES;}- (void)collectionView:(UICollectionView *)colView didHighlightItemAtIndexPath:(NSIndexPath *)indexPath{    UICollectionViewCell* cell = [colView cellForItemAtIndexPath:indexPath];        [cell setBackgroundColor:[UIColor purpleColor]];}- (void)collectionView:(UICollectionView *)colView  didUnhighlightItemAtIndexPath:(NSIndexPath *)indexPath{    UICollectionViewCell* cell = [colView cellForItemAtIndexPath:indexPath];        [cell setBackgroundColor:[UIColor yellowColor]];} ------------------------------------------------------------------------------------------------------------- 3

 
ios的UICollectionView并不能在iOS6之前的版本中使用,为了兼容之前的版本需要自定义UICollectionView。写完之后发现人家已经有开源了,下过来看了看发现我是用UIScrollerView的委托真是多此一举,完全可以用layout来实现嘛。我在判断重用的时候用了一大堆if没有别人写的简洁明了。
 
首先是定义委托,需要用户传入collection总item的总数与每一行中item的个数。其余的与UITableView的委托基本一致。
isNeedRefreshOrMore方法用来判断用户使用需要下拉刷新上拉更多的功能,返回YES就使用。
doCollectionRefresh即为响应下拉刷新事件。更多同样。。
1 #pragma mark - 2 #pragma mark 委托 3 @protocol CustomCollectionDataSource
4 @required 5 //item的总个数 6 -(NSInteger)numberOfItemsInCollection; 7 //每一行的个数 8 -(NSInteger)numberOfItemsInRow; 9 @end10 11 @protocol CustomCollectionDelegate
12 @required13 -(CustomCollectionItem *)itemInCollectionAtPoint:(CGPoint)point collectionView:(CustomCollectionView *)collection;14 @optional15 -(void)itemDidSelectedAtPoint:(CGPoint)point;16 -(BOOL)isNeedRefreshOrMore;17 -(void)doCollectionRefresh;18 -(void)doCollectionMore;19 20 -(UIView *)collectionViewForHeader;21 @end
在同文件中定义了页面状态的枚举,用来区分Collcetion的状态。同时定义了一些public方法
#import 
typedef enum { CS_Init, CS_More, CS_Refresh}CollectionState;@class CustomCollectionItem;@protocol CustomCollectionDataSource;@protocol CustomCollectionDelegate;@interface CustomCollectionView : UIScrollView
@property (weak, nonatomic) id
dataSource;@property (weak, nonatomic) id
customDelegate;-(CustomCollectionItem *)dequeueReusableItemWithIdentifier:(NSString *)identifier;-(void)addItemsIntoDic;-(void)itemClickedAtPoint:(CGPoint)point;-(void)reloadData;-(CustomCollectionItem *)getItemAtPoint:(CGPoint)point;@property (strong,nonatomic) UIView *headerView;@end
#import "CustomCollectionView.h"#import "CustomCollectionItem.h"#import "BaseRMView.h"@interface CustomCollectionView() //重用池--原谅这个名字@property (strong, nonatomic) NSMutableDictionary *contentItemDictionary; //能够显示的item数量(以行计)@property(assign,nonatomic) NSInteger showCount;@property (assign,nonatomic) NSInteger offRowIndex; //分割线--没有用到 默认成了10px@property (assign,nonatomic) CGFloat itemSpliteWidth; //总行数@property (assign,nonatomic) NSInteger rows;//item个数@property (assign, nonatomic) NSInteger numberOfItemsInCollection; //每行item个数@property (assign,nonatomic) NSInteger numberOfItemsInRow; //每一行的高度@property (assign, nonatomic) CGFloat heightOfRow;//@property (assign, nonatomic) CGRect viewFrame;//是否第一次加载@property (assign,nonatomic) BOOL isFirstLoad; //上一次scrollview的offsetY,用来判断是向上滑动还是向下滑动@property (assign,nonatomic) CGFloat lastOffsetY; //当前最后一行的index,从0开始@property (assign,nonatomic) NSInteger lastRowIndex; //当前最上一行的index,从0开始@property (assign,nonatomic) NSInteger topRowIndex;//没用@property (assign,nonatomic) NSInteger numberOfMore;//是否需要显示刷新更多页面标志@property (assign,nonatomic) BOOL isNeedShowMoreTag; //刷新view@property (strong,nonatomic) BaseRMView *refreshView; //更多view@property (strong,nonatomic) BaseRMView *moreView;@property (assign,nonatomic) CGFloat baseOffsetY;@property (assign,nonatomic) CGFloat baseCanMove;//reload之前的行数,上拉更多的时候如果用户滑动的距离超过行高会出错,用beforeRowCount来比较rows来判断新增的item需要添加的坐标@property (assign,nonatomic) NSInteger beforeRowCount;//@property (assign,nonatomic) NSInteger firstShowCount;@end
#pragma mark -#pragma mark 页面初始化-(id)init{    CGRect frame=[UIScreen mainScreen].applicationFrame;    self=[self initWithFrame:frame];    if(self){            }    return self;}- (id)initWithFrame:(CGRect)frame{    self = [super initWithFrame:frame];    if (self) {        _viewFrame=frame;        self.delegate=self;        _isFirstLoad=YES;        _contentItemDictionary=[[NSMutableDictionary alloc] init];        _isNeedShowMoreTag=NO;    }    return self;}
#pragma mark -#pragma mark 数据初始化-(void)loadData{    if ([_dataSource respondsToSelector:@selector(numberOfItemsInCollection)]) {        _numberOfItemsInCollection=[_dataSource numberOfItemsInCollection];    }else{        _numberOfItemsInCollection=0;    }    if([_dataSource respondsToSelector:@selector(numberOfItemsInRow)]){        _numberOfItemsInRow=[_dataSource numberOfItemsInRow];        _heightOfRow=((300.0-(_numberOfItemsInRow-1)*10)/_numberOfItemsInRow);        _itemSpliteWidth=10;    }else{        _numberOfItemsInRow=3;//默认为3        _heightOfRow=88;        _itemSpliteWidth=18;    }    if ([_dataSource respondsToSelector:@selector(numberofMore)]) {        _numberOfMore=[_dataSource numberofMore];    }    if ([_customDelegate respondsToSelector:@selector(isNeedRefreshOrMore)]) {        _isNeedShowMoreTag=[_customDelegate isNeedRefreshOrMore];    }    if ([_customDelegate respondsToSelector:@selector(collectionViewForHeader)]) {        _headerView=[_customDelegate collectionViewForHeader];        if (![self.subviews containsObject:_headerView]) {            [self addSubview:_headerView];        }    }    //计算行数    _rows=ceil((float)_numberOfItemsInCollection/_numberOfItemsInRow);    CGFloat contentHeight=(_rows*_heightOfRow + (_rows+1)*10+_headerView.frame.size.height);    CGFloat scrollContentHeight=contentHeight>_viewFrame.size.height?contentHeight:_viewFrame.size.height;    //计算一页能显示多少行    _showCount=  (NSInteger)ceil((self.frame.size.height/(_heightOfRow+10)));    [self setContentSize:CGSizeMake(320, scrollContentHeight)];    //判断是否有新增行,如果有当前最上义行index+1    if (_rows!=_beforeRowCount&&_beforeRowCount!=0) {        _topRowIndex++;    }   //从当前最上一行开始增加showcount行的item    for (int i=_topRowIndex; i<_topRowIndex+_showCount; i++) {        [self creatItem:i];    }    if (_isNeedShowMoreTag==YES) {        if (![self.subviews containsObject:_refreshView]) {            _refreshView=[[BaseRMView alloc] initWithState:Refresh];            [_refreshView setFrame:CGRectMake(0, -50, 320, 50)];            [_refreshView setBackgroundColor:[UIColor grayColor]];            [self addSubview:_refreshView];         }
if (![self.subviews containsObject:_moreView]) {            _moreView=[[BaseRMView alloc] initWithState:More];            [_moreView setFrame:CGRectMake(0, self.contentSize.height, 320, 50)];            [_moreView setBackgroundColor:[UIColor grayColor]];            [self addSubview:_moreView];        }else{            [_moreView setFrame:CGRectMake(0, self.contentSize.height, 320, 50)];        }    }}

 

-(void)layoutSubviews{
//第一次加载时初始化数据,之后不需要重新计算 if (_isFirstLoad) { [self loadData]; //offsetY基数 只在第一次移动时候,10为默认的分割线高度 _baseOffsetY=(10*(_showCount+1)+_heightOfRow*_showCount)-self.frame.size.height; //移动基数 _baseCanMove=10+_heightOfRow; _isFirstLoad=NO; _lastRowIndex=_showCount-1; _topRowIndex=0; }}
//重新加载数据,记录加载前的行数 -(void)reloadData{    _beforeRowCount=_rows;    [self loadData];}
#pragma mark -#pragma mark Item相关-(void)creatItem:(NSInteger)rowIndex{    if ([_customDelegate respondsToSelector:@selector(itemInCollectionAtPoint:collectionView:)]) {        for (int j=0; j<_numberOfItemsInRow; j++) {            //判断当前个数是否超过了总个数(单数情况下)            if (!(((rowIndex)*_numberOfItemsInRow+j+1)>_numberOfItemsInCollection)) {                //根据委托创建item                CustomCollectionItem *item=[_customDelegate itemInCollectionAtPoint:CGPointMake(rowIndex, j) collectionView:self];                //设置item的大小                [item setFrame:CGRectMake(10+_heightOfRow*j+_itemSpliteWidth*j, 10+_heightOfRow*rowIndex+10*rowIndex+_headerView.frame.size.height, _heightOfRow, _heightOfRow)];                //设置item的point坐标                item.point=CGPointMake(rowIndex, j);                //在view中加入item                [self addSubview:item];            }        }    }}
//根据重用标志(reuseidentifier)从重用池中获取item-(CustomCollectionItem *)dequeueReusableItemWithIdentifier:(NSString *)identifier{    NSArray *cellArray=[self.contentItemDictionary objectForKey:identifier];    if (cellArray.count==0) {        return nil;    }else{        id firstObject=[cellArray objectAtIndex:0];        if([firstObject isKindOfClass:[CustomCollectionItem class]]){            //获取item后从重用池中删除item;            CustomCollectionItem *item=firstObject;            [[self.contentItemDictionary objectForKey:identifier] removeObject:firstObject];            [item reset];            return item;        }else{            return nil;        }    }}
//根据point坐标从当前item数组中获取item-(CustomCollectionItem *)getItemAtPoint:(CGPoint)point{    CustomCollectionItem *result=nil;    for (id item in self.subviews) {        if ([item isKindOfClass:[CustomCollectionItem class]]) {            if (((CustomCollectionItem *)item).point.x==point.x                && ((CustomCollectionItem *)item).point.y==point.y) {                result=item;            }        }    }    return result;}
-(void)addItemToPool:(CustomCollectionItem *)item{    if([[self.contentItemDictionary allKeys] containsObject:item.reuseIdentifier]){        [[self.contentItemDictionary objectForKey:item.reuseIdentifier] addObject:item];    }else{        NSMutableArray *cellArray=[NSMutableArray arrayWithObject:item];        [self.contentItemDictionary setObject:cellArray forKey:item.reuseIdentifier];    }}
#pragma mark -#pragma mark 页面滚动//topRowIndex   ---> 当前最上一行的index(从0开始);//lastRowIndex  ---> 当前最后一行的index//removeIndex   ---> 当前被移除的最后一行的行数(从1开始)//addIndex      ---> 在showcount基础上增加的行数-(void)scrollViewDidScroll:(UIScrollView *)scrollView{    @try {        //手指向上移动移动基数后将显示下一页面        //手指向下移动移动基数将移除最下一行        BOOL isMoveUp=TRUE;//是否向下滑        if (scrollView.contentOffset.y-_lastOffsetY>0) {            isMoveUp=FALSE;        }else{            isMoveUp=TRUE;        }        _lastOffsetY=scrollView.contentOffset.y;                //刷新更多        if (scrollView.contentOffset.y==0) {            if ([self.subviews containsObject:_refreshView]) {                [_refreshView changeState:Refresh];            }        }else if(scrollView.contentOffset.y==scrollView.contentSize.height-scrollView.frame.size.height){            if ([self.subviews containsObject:_moreView]) {                [_moreView changeState:More];            }        }else if (scrollView.contentOffset.y>(scrollView.contentSize.height-scrollView.frame.size.height) ||            scrollView.contentOffset.y<0) {            if (scrollView.contentOffset.y>=(scrollView.contentSize.height-scrollView.frame.size.height+50)) {                if ([self.subviews containsObject:_moreView]&&_moreView.viewState==More) {                    [_moreView changeState:ToMore];                }            }else if (scrollView.contentOffset.y<-50){                if ([self.subviews containsObject:_refreshView]&&_refreshView.viewState==Refresh) {                    [_refreshView changeState:ToRefresh];                }            }        }else{            //判断重用            if (scrollView.contentOffset.y>_headerView.frame.size.height) {                CGFloat realMove=scrollView.contentOffset.y-_headerView.frame.size.height;                //增加的row坐标 初始为0 移动一个移动基数后加/减1                NSInteger addIndex=ceil((realMove-_baseOffsetY)/_baseCanMove);                //删除的row坐标 初始为0 移动一个移动基数后加/减1                NSInteger removeIndex=(realMove/_baseCanMove);                                //手指向上移动                if (!isMoveUp) {                    //如果最后一行编号==增加的row坐标+1&&增加的row坐标
<总行数-1 if (_lastrowindex="=addIndex+_showCount-2&&addIndex<_rows-1)" { 最后一行坐标++ _lastrowindex++; 如果最后一行坐标!="总行数;如果相等则为最后一行不需要增加" (_lastrowindex!="_rows)" [self creatitem:_lastrowindex]; } 如果删除的row坐标!="0&&删除的row坐标!=最上一行坐标&&最上一行坐标<总行数-显示行数" (removeindex!="0&&removeIndex!=_topRowIndex&&_topRowIndex<_rows-_showCount)" for (int i="0;" i<_numberofitemsinrow; i++) customcollectionitem *item="[self" getitematpoint:cgpointmake(removeindex-1, i)]; (item!="nil)" additemtopool:item]; [item removefromsuperview]; _toprowindex++; }else{ remove-->
add add-->remove if (removeIndex==_topRowIndex-1) { [self creatItem:removeIndex]; _topRowIndex--; } if (addIndex!=0&&addIndex!=_lastRowIndex-_showCount+1) { if (_lastRowIndex==_rows) { _lastRowIndex--; }else{ for (int i=0; i<_numberOfItemsInRow; i++) { CustomCollectionItem *item=[self getItemAtPoint:CGPointMake(_lastRowIndex, i)]; if (item!=nil) { [self addItemToPool:item]; [item removeFromSuperview]; } } _lastRowIndex--; } } } } } } @catch (NSException *exception) { NSLog(@"customCollectionView exception %@",exception.reason); }}
#pragma mark-#pragma mark item点击-(void)itemClickedAtPoint:(CGPoint)point{    if ([_customDelegate respondsToSelector:@selector(itemDidSelectedAtPoint:)]) {        [_customDelegate itemDidSelectedAtPoint:point];    }}
#pragma mark-#pragma mark 刷新更多-(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{    if (scrollView.contentOffset.y<-50) {        if (_isNeedShowMoreTag==YES&&[self.subviews containsObject:_refreshView]) {            if ([_customDelegate respondsToSelector:@selector(doCollectionRefresh)]) {                [_customDelegate doCollectionRefresh];            }            [_refreshView changeState:EndRefresh];        }    }else if (scrollView.contentOffset.y>scrollView.contentSize.height-scrollView.frame.size.height+50){        if (_isNeedShowMoreTag==YES&&[self.subviews containsObject:_moreView]) {            if ([_customDelegate respondsToSelector:@selector(doCollectionMore)]) {                [_customDelegate doCollectionMore];            }            [_moreView changeState:EndMore];        }    }}
#import 
#import
#import
@interface CustomCollectionItem : UIView
@property (strong,nonatomic) UIImageView *backgroundImage;@property (strong,nonatomic) NSString *reuseIdentifier;@property (assign,nonatomic) CGPoint point;-(id)initWithReuseIdentifier:(NSString *)identifier;-(void)itemTaped;-(void)reset;@end
#import "CustomCollectionItem.h"#import "CustomCollectionView.h"@interface CustomCollectionItem()@property(strong,nonatomic) UIView *contentView;@end@implementation CustomCollectionItem-(id)initWithReuseIdentifier:(NSString *)identifier{    self=[super init];    if (self) {        _reuseIdentifier=identifier;        [self setUserInteractionEnabled:YES];        _backgroundImage= [[UIImageView alloc] init];    }    return self;}-(void)setFrame:(CGRect)frame {    [super setFrame:frame];    [_backgroundImage setFrame:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];    _backgroundImage.tag=10099;}-(void)layoutSubviews {    [super layoutSubviews];    if([self viewWithTag:10099]== nil)   {        [self addSubview:_backgroundImage];        [self sendSubviewToBack:_backgroundImage];    }    UITapGestureRecognizer *tapGR=[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(itemTaped)];    [self addGestureRecognizer:tapGR];}-(void)itemTaped{    [(CustomCollectionView *)[self superview] itemClickedAtPoint:self.point];}-(void)setBackgroundImage:(UIImageView *)backgroundImage {    _backgroundImage=backgroundImage;}#pragma override-(void)reset{    }- (void)encodeWithCoder:(NSCoder*)coder{    Class clazz = [self class];    u_int count;        objc_property_t* properties = class_copyPropertyList(clazz, &count);    NSMutableArray* propertyArray = [NSMutableArray arrayWithCapacity:count];    for (int i = 0; i < count ; i++)    {        const char* propertyName = property_getName(properties[i]);        [propertyArray addObject:[NSString  stringWithCString:propertyName encoding:NSUTF8StringEncoding]];    }    free(properties);            for (NSString *name in propertyArray)    {        id value = [self valueForKey:name];        [coder encodeObject:value forKey:name];    }}- (id)initWithCoder:(NSCoder*)decoder{    if (self = [super init])    {        if (decoder == nil)        {            return self;        }                Class clazz = [self class];        u_int count;                objc_property_t* properties = class_copyPropertyList(clazz, &count);        NSMutableArray* propertyArray = [NSMutableArray arrayWithCapacity:count];        for (int i = 0; i < count ; i++)        {            const char* propertyName = property_getName(properties[i]);            [propertyArray addObject:[NSString  stringWithCString:propertyName encoding:NSUTF8StringEncoding]];        }        free(properties);                        for (NSString *name in propertyArray)        {            id value = [decoder decodeObjectForKey:name];            [self setValue:value forKey:name];        }    }    return self;}@end

转载于:https://www.cnblogs.com/LifeTechnologySupporter/p/5037806.html

你可能感兴趣的文章
eclipse工程 'cocostudio/CocoStudio.h' file not found
查看>>
045医疗项目-模块四:采购单模块—采购单提交(Dao,Service,Action三层)
查看>>
dockerfile创建php容器(安装memcached、redis、gd、xdebug扩展)
查看>>
转:面对JXTA,我迷茫了
查看>>
IT人必须学会的职场四原则
查看>>
Android之剪贴薄实现
查看>>
Sonix SN9P701 OCR点读笔二维码识别源码
查看>>
oracle 单引号 双引号 连接符
查看>>
如何使用fileupload工具来实现文件上传
查看>>
EZ GUI Button和Checkbox创建
查看>>
指针[收藏]
查看>>
审批流程设计方案-介绍(一)
查看>>
Python多进程编程
查看>>
使Eclipse下支持编写HTML/JS/CSS/JSP页面的自动提示。
查看>>
IIS_右键点击浏览网站没有反应
查看>>
POJ训练计划1035_Spell checker(串处理/暴力)
查看>>
Makefile 使用总结【转】
查看>>
一起学微软Power BI系列-官方文档-入门指南(4)Power BI的可视化
查看>>
Android.util.Log 关于Android开发中打印log
查看>>
转:Python yield 使用浅析 from IBM Developer
查看>>