ios开发——用UITableView展示多行多列图片

来源:互联网 发布:即时聊天软件 行业 编辑:程序博客网 时间:2024/04/29 16:09

有时我们可能需要用UITableView通过自定义Cell的方法来展示多行多列的图片,实现类似与UICollectionView的效果。例如电子书等应用。这里我们来实现这个功能。


首先可以自定义一个UIView来代表Cell内的每一个Item,然后以每行三列为例,将其布局到每个cell上。


对这个自定义cell,暴露出一个NSArray属性groupedItem(已分好组的Item)表示有三个Item的数组,这样在TableView中只要cell.groupedItem = ……,在cell中将这三个Item拿出来,分别为View提供数据,让View展示数据即可。

因此关键就是将数据模型(Model)分成2维数组,每个子数组有三个元素。

当从网络上或本地获取所有数据保存到字典中后,可以对这个数组每三个元素加入到一个新数组,然后新数组加入到最终的二维数组。即

    <span style="font-size:14px;">NSMutableArray *tempArray = nil;    for (int i = 0; i < self.allBooks.count; i++) {        if (i % 3 == 0) {            tempArray = [NSMutableArray arrayWithCapacity:3];            [self.bookGroup addObject:tempArray];        }                [tempArray addObject:[self.allBooks objectAtIndex:i]];    }</span>

allBooks代表保存所有数据的数组,bookGroup表示最终的二维数组。


分好组以后就可以在tableview数据源协议方法中为cell提供数据

<span style="font-size:14px;">- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{    NSString *cellId = @"CYZBookShelfViewCell";    CYZBookCell *cell = [tableView dequeueReusableCellWithIdentifier:cellId];        if (cell == nil) {        cell = [[CYZBookCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellId];    }        cell.booksGroup = [self.bookGroup2D objectAtIndex:indexPath.row];        return cell;}</span>



在Cell中定义一个数组来接收该行显示的三个Model

<span style="font-size:14px;">@interface CYZBookCell : UITableViewCell/**本cell要展示的三本书*/@property (strong, nonatomic) NSArray *booksGroup;@end</span>

然后再initView时建立三个子视图并加入到cell.contentView中,分别为这三个view赋tag值以便在layoutSubview中取得。接着在layoutSubview中设置这三个view的位置、数据等信息。


   <span style="font-size:14px;"> for (int i = 0; i < self.booksGroup.count; i++) {        CYZBookItemView *item = (CYZBookItemView *)[self.contentView viewWithTag:100 + i];        item.frame = CGRectMake(kHorizontalEdge + (kHorizontalEdge + 80) * i, kVerticalEdge, 80, 150);        item.hidden = NO;       //有数据的item不隐藏        item.bookModel = [self.booksGroup objectAtIndex:i];        [self addSubview:item];                //如果不添加以下方法,那么在cell复用时将不会调用子视图的layoutSubview方法,这样数据就无法更新了。        [item setNeedsLayout];        [item layoutIfNeeded];    }</span>


为什么要再layoutSubview中调用item的setNeedsLayout方法呢?这是因为Cell的复用问题,当cell复用时并不会调用item的layoutSubview方法,因此上拉tableview时展示出来的数据全是与之前重复的。所以我们需要手动让item重新布置子视图。

另一个由cell复用机制引发的问题:依旧是数据重复

我们的数据一般是从网上来的,故数据的数量并不确定,很有可能不是恰好为3的倍数。例如有40个,这是你会发现,本来最后一样应该只有一个数据,却出现了三个,并且后两个是重复的。这就是cell复用引发的另一个问题,为了解决这一问题,我们应该让无数据的item隐藏。怎么判断item有没有数据呢?在上述for循环中,self.booksGroup中保存了有数据的item,因此能进入for循环的即为有数据的。在这里让item显示。

因此,我们可以复写booksGroup的setter,让所有的数据默认隐藏。

<span style="font-size:14px;">- (void)setBooksGroup:(NSArray *)booksGroup{    _booksGroup = booksGroup;        for (int i = 0; i < booksGroup.count; i++) {        CYZBookItemView *item = (CYZBookItemView *)[self.contentView viewWithTag:100 + i];        //由于cell的复用问题,当数据并不为3的整数倍时第三个item会填充之前的数据,造成数据重复        //为了修正这个问题,没有数据的item应该隐藏,故这里将所有的item隐藏,在layoutSubview方法中有数据的item不隐藏        item.hidden = YES;    }}</span>


总结一下:

1、我们需要一个自定义视图Item表示在每行cell中的每一列,item可以包含图片、lable……自定义视图,主要功能为展示数据,只要将model对象给他,他就能展示model中的数据。

2、同时还需要一个自定义cell来盛下item,每个cell可以容纳多列,只要传给他一组数组,里面包含这一行的每一列的model对象即可,然后cell将每一个model分别给每一个item。

3、在tableView或其他控制器中将所有数据分组,分成二维数组,每一维都是每一行cell中的所有model,然后在tableview数据源协议中将二维数组的每一个元素(一维数组)分别给每一行让其显示该行的数据。

4、注意在这一过程中由于cell复用引发的一系列数据重复的问题,解决方法:1)手动调用item的setNeedsLayout方法让item刷新。2)让没数据的item隐藏,有数据的item显示。

0 0