UIScrollView实现重用机制(类似多列tableview)

本文介绍了如何为UIScrollView实现类似于UITableView的重用机制,以避免在显示多列数据时因无重用导致的内存浪费。在iOS6之前,通常通过自定义cell来处理,而iOS6引入了UICollectionView。不过,当直接使用UIScrollView时,需要手动处理子视图的移除和重用,以确保只有在屏幕上的视图才被加载,从而提高性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

要显示多列数据,在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,只是简单的提供一个思路,如果投入使用的话还得进行一些细微的调整,例如,后台加载数据,以及等待数据加载时应该转菊花来增加用户体验等。

上传两张效果图



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值