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

 

你可能感兴趣的:(Collection)