要显示多列数据,在ios6之前可以通过自定义cell来实现,在ios6之后则可以使用UICollectionView。当然也有一种办法就是直接使用UIScrollView。不过UIScrollView并不像UITableview一样有重用机制,如果不经处理就加载多条数据,就会大量占用内存,程序越来越卡。本文的主题就是为UIScrollView实现重用机制,避免内存浪费。
总体的思路比较简单,就是在移动UIScrollView时判断其子视图在不在屏幕上,不在的话就将其移动。
下面直接上代码
@interface WLViewController : UIViewController<UIScrollViewDelegate>{
NSInteger cellViewHeight;
NSInteger cellViewWide;
NSInteger countOfRow; //一行的view个数
NSInteger queueCount; //队列里元素的个数
NSInteger dataIndex;
NSInteger maxRow;
NSInteger queueIndex;
NSInteger preIndex;
NSInteger maxindex;
}
-(void)loadDataOfCell:(UIView *) cell;
-(void) cellHasBeensSlected:(NSInteger)inedx;
-(void)writeCell:(UIView *)cell;
-(void)readCell:(UIView *)cell withIndex:(NSInteger)index;
@interface WLViewController ()
@property (nonatomic,strong) NSMutableArray *dataSure; //用来存放数据,应该是存做文件,为了简单,就先放在数组里
@property (nonatomic,strong) NSMutableArray *viewQueue; //队列,通过更改队列里视图的矩形位置来实现滚动
@property (nonatomic,strong) UIScrollView *mainView;
@end
@implementation WLViewController
-(id)init
{
return [self initWithCountOfRow:4];
}
-(id)initWithCountOfRow:(NSInteger)count
{
self = [super init];
if(self)
{
countOfRow =count;
cellViewHeight = cellViewWide = 320 / countOfRow;
queueCount = countOfRow * 800 / cellViewHeight;
queueCount -= countOfRow % 2 == 0 ? 0 : countOfRow / 2;
self.dataSure = [[NSMutableArray alloc] init];
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.viewQueue = [[NSMutableArray alloc] init];
self.mainView = [[UIScrollView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.mainView.contentSize = CGSizeMake(320, 800);
self.mainView.delegate = self;
[self.view addSubview:self.mainView];
for(int i = 0; i < queueCount; i++)
{
CGRect rect = CGRectMake(i % countOfRow * cellViewWide, i / countOfRow * cellViewHeight, cellViewWide, cellViewHeight);
AlbumView *album = [[AlbumView alloc] initWithFrame:rect];
[self.mainView addSubview:album];
[self.viewQueue addObject:album];
[self loadDataOfCell:album];
dataIndex++;
}
maxRow = 0;
queueIndex = 0;
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
//防止开始就向上滑
if(scrollView.contentOffset.y <= 0)
return;
int p = (int)scrollView.contentOffset.y / cellViewHeight;
if(p >= maxRow)
{
//增加MAINVIEW的长度
[self addRows:p / 2 + 1];
maxRow = (self.mainView.contentSize.height - cellViewHeight * queueCount / countOfRow) / cellViewHeight;
}
if(p > preIndex)
{
[self pullDown:p-preIndex];
}
else if (p < preIndex)
{
[self pullUp:preIndex - p];
}
preIndex = p; //记录前一位置
if(dataIndex >= maxindex) maxindex = dataIndex;
}
-(void)addRows:(NSInteger)counts
{
CGSize size = self.mainView.contentSize;
size.height += cellViewHeight * counts;
self.mainView.contentSize = size;
}
-(void)pullDown:(NSInteger)count
{
for(int i = 0; i < countOfRow * count; i++)
{
AlbumView *album = [self.viewQueue objectAtIndex:queueIndex];
album.frame = CGRectMake(dataIndex % countOfRow * cellViewWide, dataIndex / countOfRow * cellViewHeight, cellViewWide, cellViewHeight);
//如果没加载数据,就加载数据,如果已经加载过(向上滑动后又滑下),因为已经加载过一次,所以就直接读取
if(dataIndex >= maxindex)
[self loadDataOfCell:album];
else
[self readCell:album withIndex:dataIndex];
dataIndex++;
//变更换队列的索引,指向下一个将要使用的视图
queueIndex = queueIndex + 1 == queueCount ? 0: queueIndex + 1;
}
}
-(void)pullUp:(NSInteger)count
{
for(int i = 0; i < countOfRow * count; i++)
{
dataIndex--;
queueIndex = queueIndex == 0 ? queueCount - 1: queueIndex - 1;
AlbumView *album = [self.viewQueue objectAtIndex:queueIndex];
album.frame = CGRectMake(dataIndex % countOfRow * cellViewWide, (dataIndex / countOfRow - queueCount / countOfRow ) * cellViewHeight, cellViewWide, cellViewWide);
//向上滑动只需要读取之前加载的视图
[self readCell:album withIndex:dataIndex - queueCount];
}
}
-(void)loadDataOfCell:(UIView *)cell
{
//获取数据源,如果从文件获取,则需要判断是否已经存在该文件。这里只是简化的写法。
[(AlbumView *)cell dosome];
((AlbumView *)cell).i = dataIndex;
[self writeCell:cell];
}
-(void) cellHasBeensSlected:(NSInteger)inedx
{
//钩子方法,在MAINVIEW的子视图被点击时调用
NSLog(@"------%d",inedx);
}
-(void)writeCell:(UIView *)cell
{
//写方法,这里只简单的把视图的数据写入数组
AlbumView *album = [[AlbumView alloc] init];
album.backgroundColor = cell.backgroundColor;
album.i = dataIndex;
[self.dataSure addObject:album];
}
-(void)readCell:(UIView *)cell withIndex:(NSInteger)index
{
/// /读方法,同上
AlbumView *subview = [self.dataSure objectAtIndex:index];
cell.backgroundColor = subview.backgroundColor;
((AlbumView *)cell).i = subview.i;
}
@end
为了实现钩子方法,这里选择写UIScrollView的分类的方式实现
@implementation UIScrollView (AlbumViewController)
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
if(![self.nextResponder.nextResponder isMemberOfClass:[WLViewController class]]) return;
AlbumView *album = (AlbumView *)[self hitTest:[touch locationInView:self] withEvent:event];
[(WLViewController *)self.nextResponder.nextResponder cellHasBeensSlected:album.i];
}
@end
最后是AlbumView类,这个类很随意。
@interface AlbumView : UIView
@property (nonatomic) NSInteger i;
-(void)dosome;
@end
@implementation AlbumView
-(void)dosome
{
CGFloat r = (CGFloat)(arc4random() % 10) / 10.0;
CGFloat g = (CGFloat)(arc4random() % 10) / 10.0;
CGFloat b = (CGFloat)(arc4random() % 10) / 10.0;
self.backgroundColor = [UIColor colorWithRed:r green:g blue:b alpha:1.0];
}
@end
一个简单的dome,只是简单的提供一个思路,如果投入使用的话还得进行一些细微的调整,例如,后台加载数据,以及等待数据加载时应该转菊花来增加用户体验等。
上传两张效果图