zoukankan      html  css  js  c++  java
  • iOS-UICollectionView

    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

    UICollectionView基础

     

    初始化部分:

    复制代码
    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, UICollectionViewDataSource>

    因为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 <UIKit/UIKit.h>
    
    @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的实现

     
    ios的UICollectionView并不能在iOS6之前的版本中使用,为了兼容之前的版本需要自定义UICollectionView。写完之后发现人家已经有开源了,下过来看了看发现我是用UIScrollerView的委托真是多此一举,完全可以用layout来实现嘛。我在判断重用的时候用了一大堆if没有别人写的简洁明了。
     
    首先是定义委托,需要用户传入collection总item的总数与每一行中item的个数。其余的与UITableView的委托基本一致。
    isNeedRefreshOrMore方法用来判断用户使用需要下拉刷新上拉更多的功能,返回YES就使用。
    doCollectionRefresh即为响应下拉刷新事件。更多同样。。
    复制代码
     1 #pragma mark -
     2 #pragma mark 委托
     3 @protocol CustomCollectionDataSource<NSObject>
     4 @required
     5 //item的总个数
     6 -(NSInteger)numberOfItemsInCollection;
     7 //每一行的个数
     8 -(NSInteger)numberOfItemsInRow;
     9 @end
    10 
    11 @protocol CustomCollectionDelegate<NSObject>
    12 @required
    13 -(CustomCollectionItem *)itemInCollectionAtPoint:(CGPoint)point collectionView:(CustomCollectionView *)collection;
    14 @optional
    15 -(void)itemDidSelectedAtPoint:(CGPoint)point;
    16 -(BOOL)isNeedRefreshOrMore;
    17 -(void)doCollectionRefresh;
    18 -(void)doCollectionMore;
    19 
    20 -(UIView *)collectionViewForHeader;
    21 @end
    复制代码
    在同文件中定义了页面状态的枚举,用来区分Collcetion的状态。同时定义了一些public方法
    复制代码
    #import <UIKit/UIKit.h>
    
    typedef enum {
        CS_Init,
        CS_More,
        CS_Refresh
    }CollectionState;
    
    @class CustomCollectionItem;
    @protocol CustomCollectionDataSource;
    @protocol CustomCollectionDelegate;
    
    @interface CustomCollectionView : UIScrollView<UIScrollViewDelegate>
    @property (weak, nonatomic) id<CustomCollectionDataSource> dataSource;
    @property (weak, nonatomic) id<CustomCollectionDelegate> 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++;
                            //如果最后一行坐标!=总行数;如果相等则为最后一行不需要增加
                            if (_lastRowIndex!=_rows) {
                                [self creatItem:_lastRowIndex];
                            }
                        }
                        //如果删除的row坐标!=0&&删除的row坐标!=最上一行坐标&&最上一行坐标<总行数-显示行数
                        if (removeIndex!=0&&removeIndex!=_topRowIndex&&_topRowIndex<_rows-_showCount) {
                            for (int i=0; i<_numberOfItemsInRow; i++) {
                                CustomCollectionItem *item=[self getItemAtPoint:CGPointMake(removeIndex-1, i)];
                                if (item!=nil) {
                                    [self 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 <UIKit/UIKit.h>
    #import <objc/runtime.h>
    #import <Foundation/Foundation.h>
    @interface CustomCollectionItem : UIView<UIGestureRecognizerDelegate,NSCoding>
    @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
  • 相关阅读:
    Matlab练习——素数查找
    Matlab 三维绘图与统计绘图
    Matlab练习——多项式和一元方程求解
    Matlab 矩阵函数
    Malab 常用数学函数
    Matlab练习——矩阵和数组的操作
    Matlab 图像处理入门
    Matlab 二维绘图函数(plot类)
    Matlab 曲线拟合之polyfit与polyval函数
    android 画虚线、实线,画圆角矩形,一半圆角
  • 原文地址:https://www.cnblogs.com/LifeTechnologySupporter/p/5037806.html
Copyright © 2011-2022 走看看