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
相關文章
相關標籤/搜索