iOS UICollectionView的实现
#pragma mark -
#pragma mark 委托
@protocol CustomCollectionDataSource<NSObject>
@required
//item的总个数
-(NSInteger)numberOfItemsInCollection;
//每一行的个数
-(NSInteger)numberOfItemsInRow;
@end @protocol CustomCollectionDelegate<NSObject>
@required
-(CustomCollectionItem *)itemInCollectionAtPoint:(CGPoint)point collectionView:(CustomCollectionView *)collection;
@optional
-(void)itemDidSelectedAtPoint:(CGPoint)point;
-(BOOL)isNeedRefreshOrMore;
-(void)doCollectionRefresh;
-(void)doCollectionMore; -(UIView *)collectionViewForHeader;
@end
#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=;
}
if([_dataSource respondsToSelector:@selector(numberOfItemsInRow)]){
_numberOfItemsInRow=[_dataSource numberOfItemsInRow];
_heightOfRow=((300.0-(_numberOfItemsInRow-)*)/_numberOfItemsInRow);
_itemSpliteWidth=;
}else{
_numberOfItemsInRow=;//默认为3
_heightOfRow=;
_itemSpliteWidth=;
}
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+)*+_headerView.frame.size.height);
CGFloat scrollContentHeight=contentHeight>_viewFrame.size.height?contentHeight:_viewFrame.size.height;
//计算一页能显示多少行
_showCount= (NSInteger)ceil((self.frame.size.height/(_heightOfRow+)));
[self setContentSize:CGSizeMake(, scrollContentHeight)];
//判断是否有新增行,如果有当前最上义行index+1
if (_rows!=_beforeRowCount&&_beforeRowCount!=) {
_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(, -, , )];
[_refreshView setBackgroundColor:[UIColor grayColor]];
[self addSubview:_refreshView];
}
if (![self.subviews containsObject:_moreView]) {
_moreView=[[BaseRMView alloc] initWithState:More];
[_moreView setFrame:CGRectMake(, self.contentSize.height, , )];
[_moreView setBackgroundColor:[UIColor grayColor]];
[self addSubview:_moreView];
}else{
[_moreView setFrame:CGRectMake(, self.contentSize.height, , )];
}
}
}
-(void)layoutSubviews{
//第一次加载时初始化数据,之后不需要重新计算
if (_isFirstLoad) {
[self loadData];
//offsetY基数 只在第一次移动时候,10为默认的分割线高度
_baseOffsetY=(*(_showCount+)+_heightOfRow*_showCount)-self.frame.size.height;
//移动基数
_baseCanMove=+_heightOfRow;
_isFirstLoad=NO;
_lastRowIndex=_showCount-;
_topRowIndex=;
}
}
//重新加载数据,记录加载前的行数
-(void)reloadData{
_beforeRowCount=_rows;
[self loadData];
}
#pragma mark -
#pragma mark Item相关
-(void)creatItem:(NSInteger)rowIndex{
if ([_customDelegate respondsToSelector:@selector(itemInCollectionAtPoint:collectionView:)]) {
for (int j=; j<_numberOfItemsInRow; j++) {
//判断当前个数是否超过了总个数(单数情况下)
if (!(((rowIndex)*_numberOfItemsInRow+j+)>_numberOfItemsInCollection)) {
//根据委托创建item
CustomCollectionItem *item=[_customDelegate itemInCollectionAtPoint:CGPointMake(rowIndex, j) collectionView:self];
//设置item的大小
[item setFrame:CGRectMake(+_heightOfRow*j+_itemSpliteWidth*j, +_heightOfRow*rowIndex+*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==) {
return nil;
}else{
id firstObject=[cellArray objectAtIndex:];
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>) {
isMoveUp=FALSE;
}else{
isMoveUp=TRUE;
}
_lastOffsetY=scrollView.contentOffset.y; //刷新更多
if (scrollView.contentOffset.y==) {
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<) {
if (scrollView.contentOffset.y>=(scrollView.contentSize.height-scrollView.frame.size.height+)) {
if ([self.subviews containsObject:_moreView]&&_moreView.viewState==More) {
[_moreView changeState:ToMore];
}
}else if (scrollView.contentOffset.y<-){
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-&&addIndex<_rows-) {
//最后一行坐标++
_lastRowIndex++;
//如果最后一行坐标!=总行数;如果相等则为最后一行不需要增加
if (_lastRowIndex!=_rows) {
[self creatItem:_lastRowIndex];
}
}
//如果删除的row坐标!=0&&删除的row坐标!=最上一行坐标&&最上一行坐标<总行数-显示行数
if (removeIndex!=&&removeIndex!=_topRowIndex&&_topRowIndex<_rows-_showCount) {
for (int i=; i<_numberOfItemsInRow; i++) {
CustomCollectionItem *item=[self getItemAtPoint:CGPointMake(removeIndex-, i)];
if (item!=nil) {
[self addItemToPool:item];
[item removeFromSuperview];
}
}
_topRowIndex++;
}
}else{//remove-->add add-->remove
if (removeIndex==_topRowIndex-) {
[self creatItem:removeIndex];
_topRowIndex--;
}
if (addIndex!=&&addIndex!=_lastRowIndex-_showCount+) {
if (_lastRowIndex==_rows) {
_lastRowIndex--;
}else{
for (int i=; 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<-) {
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+){
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(, , self.frame.size.width, self.frame.size.height)];
_backgroundImage.tag=;
} -(void)layoutSubviews {
[super layoutSubviews];
if([self viewWithTag:]== 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 = ; 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 = ; 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
iOS UICollectionView的实现的更多相关文章
- iOS UICollectionView 长按移动cell
ref:http://www.jianshu.com/p/31d07bf32d62 iOS 9之后: 示例如下 效果 前言: 看完你可以学到哪些呢? 就是文章标题那么多, 只有那么多. . 手残效果图 ...
- iOS UICollectionView高级用法(长按自由移动cell)-新
[reference]http://www.jianshu.com/p/31d07bf32d62 iOS 9之后: 示例如下 效果 前言: 看完你可以学到哪些呢? 就是文章标题那么多, 只有那么多. ...
- iOS UICollectionView(转三)
上篇博客的实例是自带的UICollectionViewDelegateFlowLayout布局基础上来做的Demo, 详情请看<iOS开发之窥探UICollectionViewControlle ...
- iOS UICollectionView(转一) XIB+纯代码创建:cell,头脚视图 cell间距
之前用CollectionViewController只是皮毛,一些iOS从入门到精通的书上也是泛泛而谈.这几天好好的搞了搞苹果的开发文档上CollectionViewController的内容,亲身 ...
- [iOS] UICollectionView初始化滚动到中间的bug
转载请保留地址wossoneri.com 问题 首先看一下我之前写的demo:link demo是封装了一个控件,直接在MainViewController的viewWillAppear里初始化,并且 ...
- iOS UICollectionView高级用法(长按自由移动cell)
iOS 9之后: 示例如下 效果 前言: 看完你可以学到哪些呢? 就是文章标题那么多, 只有那么多. . 手残效果图没弄好. @property (nonatomic, strong) UIColle ...
- 【Swift】iOS UICollectionView 计算 Cell 大小的陷阱
前言 API 不熟悉导致的问题,想当然的去理解果然会出问题,这里记录一下 UICollectionView 使用问题. 声明 欢迎转载,但请保留文章原始出处:) 博客园:http://www.cn ...
- IOS UICollectionView基础+UICollectionViewFlowLayout基础
UICollectionView在众多控件中也算是比较常用的了,像淘宝在浏览宝贝时采用的就是UICollectionView,对于UICollectionView->UICollectionVi ...
- [IOS UICollectionView模版]
创建CollectionCell模版: 1.新建类CollectionCell继承自UICollectionViewCell 2.新建Xib,命名为CollectionCell.xib a.选中Col ...
随机推荐
- Linux网络编程-SIGPIPE信号导致的程序退出问题
当客户端close关闭连接时,若server端接着发送数据,根据TCP协议的规定,server端会收到RST响应,当server端再次往客户端发送数据时,系统会发出一个SIGPIPE信号给server ...
- LightOJ 1094 - Farthest Nodes in a Tree(树的直径)
http://acm.hust.edu.cn/vjudge/contest/121398#problem/H 不是特别理解,今天第一次碰到这种问题.给个链接看大神的解释吧 http://www.cnb ...
- 移动端调试工具推荐 小苹果和debugap
小苹果的安装很简单,这是官网,一看就会,完全小白教程. http://www.xbole.com/ debugap的也很简单,这里简单附图介绍一下 ps 官网: http://www.debuggap ...
- Windows服务的手动添加和删除方法
Windows服务的手动添加和删除方法 服务,是指执行指定系统功能的程序.例程或进程,以便支持其他程序,尤其是低层(接近硬件)程序.其实,服务就是一种特殊的应用程序,它从服务启动开始就一直处于运行状态 ...
- 用PHP实现URL转换短网址的算法示例
短网址就是把一个长的地址转换在超级短的网址,然后访问短网址即可跳转到长网址了,下面来看用PHP实现URL转换短网址的算法与例子. 短网址(Short URL) ,顾名思义就是在形式上比较短的网址.在W ...
- [linux] 线程和wait命令,sleep命令
在linux脚本编程中使用多线程编程,其中wait命令可以实现shell的多线程同步控制. 1. 启动后台子任务 在执行命令后加&操作符,表示将命令放在子shell中异步执行.可以达到多线程效 ...
- 今天想用jquery来实现div的拖放功能
html5标签.拖放(Drag 和 drop)是 HTML5 标准的组成部分. 步骤一:首先设置标签可以被拖 draggable="true" 步骤二:选取被拖的标签,和要放置被拖 ...
- UICollectionCell可移动的逻辑
- Delphi初学者,向万一老师致敬
今天首开博客园... 刚开始学习Delphi难免诸多不懂... 感谢万能的万一老师...
- php错误级别的设置方法
PHP在运行时, 针对严重程度不同的错误,会给以不同的提示. eg:在$a没声明时,直接相加,值为NULL,相加时当成0来算.但是,却提示NOTICE,即注意. 我们在开发中, 为了程序的规范性,把报 ...