如何使具有不同子视图的UITableViewCell可重用?

时间:2011-04-21 16:19:23

标签: iphone objective-c ios uitableview

我有一个UITableView,其中我自然地显示UITableViewCells所有相同的类,我们称之为MyCell。我有一个MyCell.xib,一个MyCell.h和一个MyCell.m

不幸的是,这个单元确实包含一个子视图,该子视图保存不同的内容,例如一个train subview和一个car subview。因此,如果UITableView需要新的单元格,则它始终为MyCell,但有时它包含火车子视图,有时还包含汽车子视图。

现在,这是我的问题:如何使MyCell正确重用?单元本身可以按预期重复使用(在.xib中我定义了它的标识符)但是必须为每个单元一次又一次地创建子视图。我的第一个想法是根据它的内容更改MyCell的标识符,但不幸的是,在运行时无法更改reuseIdentifier。 但是,我可以实现我自己的- (NSString *) reuseIdentifier {}我认为可行,但我不认为它很棒。有更好的方法吗?

非常感谢提前!

编辑:我意识到我需要补充一点,子视图存储在他们自己的类/ xib中,以保持他们的代码分离。

4 个答案:

答案 0 :(得分:11)

我建议您为每种类型的单元格创建自己的类,而不是将子视图添加到单元格。如果您有各种类型:火车,汽车,自行车,船和飞机,我会创建五个子类。

据我了解Apple,带有标识符的重用机制仅适用于这种情况:不同类型的单元格获取自己的标识符,而不是每个单元格都是特殊的标识符。只是指出我如何解释整个事情。

在Apple的Table View Programming Guide for iOS / Characteristics of Cell Objects中,第3个paragrpah可以深入了解重用标识符的含义。

我自己编写了一个小的TableViewCellFactory类,它使我的生活更容易使用界面构建器创建单元格,并在几分钟内将它们放在我的应用程序中。

首先是一个关于如何使用cellForRowAtIndexPath和工厂以及为单元格设置内容的小例子。

我使用工厂创建一个新的单元格,需要tableView,以便它可以处理重用逻辑。接下来就是让方法填写单元格的内容。在这种情况下,它是一个显示带有一些文本的视频剪辑的单元格。

数据源委托方法和帮助

- (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)anIndexPath
{
    VideoClipTableViewCell *cell = [TableViewCellFactory videoClipTableViewCellWithTableView:aTableView];

    [self configureVideoClipCellWithCell:cell andIndexPath:anIndexPath];

    // code to decide what kind of cell not shown, but it could be here, just move the model
    // access code from the configure cell up here and decide on what you get

    return cell;
}

接下来是将内容放入单元格的数据源助手。从我的模型数组中获取内容并设置属性。注意,这通过引用完成所有操作,不返回任何内容。

- (void)configureVideoClipCellWithCell:(VideoClipTableViewCell *)aCell andIndexPath:(NSIndexPath *)anIndexPath
{
    VideoClip *videoClip = [videoClips objectAtIndex:anIndexPath.row];

    aCell.videoTitleLabel.text = videoClip.title;
    aCell.dateLabel.text = videoClip.date;

    // more data setting ...
}

<强> TableViewFactory

这个类主要包括方便方法和一些样板方法来完成实际工作。

// Convenience static method to create a VideoClipTableViewCell
+ (VideoClipTableViewCell *)videoClipTableViewCellWithTableView:(UITableView *)aTableView
{
    return [self loadCellWithName:@"VideoClipTableViewCell" tableView:aTableView];
}

// method to simplify cell loading
+ (id)loadCellWithName:(NSString *)aName tableView:(UITableView *)aTableView
{
    return [self loadCellWithName:aName 
                        className:aName
                       identifier:aName
                        tableView:aTableView];
}

// method with actually tries to create the cell
+ (id)loadCellWithName:(NSString *)aName 
             className:(NSString *)aClassName 
            identifier:(NSString *)anIdentifier 
             tableView:(UITableView *)aTableView
{
    UITableViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:anIdentifier];

    if (cell == nil) {
        UINib * nib = [UINib nibWithNibName:aName bundle:nil];  

        NSArray * nibContent = nil;

        nibContent = [nib instantiateWithOwner:nil options:nil];

        for (id item in nibContent) {
            if ([item isKindOfClass:NSClassFromString(aClassName)]) {
                cell = item;
            }
        }
    }
    return cell;
}

我抛弃了整个错误和异常处理只是为了保持示例简短。如果有人感兴趣,我会添加代码。

关于用法的一些重要事项是:

  • 连接的类名,重用 标识符和笔尖名称都是 同样可以创建一个单元格 只有一个字符串常量,否则 长loadCellWithName必须是 使用

  • 不要忘记在界面构建器中设置重用标识符。

  • nib应该只包含一个TableViewCell(虽然可以通过一些编码进行更改)

  • 不要设置文件所有者的出口,使用tableViewCell的出口

  • 将单元格的类标识设置为应该首先创建的对应类

  • 查看截图

enter image description here

关于继承自定义单元格的想法

实际上很容易为您自己的单元格创建子类,为它添加一些属性,使它们在带有出口的IB中可用,在IB中为您的nib文件选择新的扩展类。

主要问题是界面本身。基于接口构建器中的自定义单元格,使用不同类型的单元格并不容易。第一种方法是复制nib文件,重命名它并将其与所有现有引用一起使用,并将新文件链接到不同的出口。但是如果必须改变基本单元会发生什么?经历各种遗传细胞可能是一项繁琐的工作。

我偶然发现了{可爱的Cocoa Custom views in Interface Builder using IBPlugins。这是一个很好的教程,如何在IB中扩展组件库。我们的自定义基本单元可以成为库中的项目,并成为我们一直在寻找的模板。我认为这种方法是正确的选择方式。不过,看看必要的步骤,它不是在5分钟内完成的。

接口构建器是一个有用的工具,允许我们快速创建视图,但是当通过子类化实现可重用性时,创建可维护视图需要很大的步骤。可惜。

仅使用代码创建视图我认为如果涉及多个继承级别或只有一个基本视图的许多祖先类,则子类化会更好。

修改

另一方面,Apple警告过在单元格中过度使用子视图:

  

但是,如果单元格的内容是   由三四个以上组成   子视图,滚动性能可能   遭受。在这种情况下(特别是   如果单元格不可编辑),请考虑   直接在一个子视图中绘制   单元格的内容视图。这个的要点   准则就是在实施时   自定义表视图单元格,请注意   在最优之间存在权衡   滚动性能和最佳   编辑或重新排序表现。

目前任何方法都有其缺点和优点:

  • 太人的子视图会被击中 表现,轻松完成IB

  • 使用代码绘图将导致a 难以维护代码库但会 表现更好

  • 跳过IB制作 子类化模板单元类

  • 通过子类化的层次结构 用笔尖很难实现IB 文件

答案 1 :(得分:3)

有几种不同的方法可以做到这一点。您需要一种方法来访问该子视图,并在重用时重置或更改它。

  1. 您可以将UITableViewCell子类化为您自己的单元格类,该单元格具有火车或汽车视图的属性。这样,您可以在重复使用单元格时访问和更改该视图。

  2. 为每种类型的单元格分配不同的标识符:

  3. `

    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
            static NSString *CarCellIdentifier = @"CarCell";
        static NSString *TrainCellIdentifier = @"TrainCell";
            if(indexPath == carCellNeeded) { 
            UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CarCellIdentifier];
                 if (cell == nil) {
                     cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CarCellIdentifier] autorelease]; 
                 [cell addSubview:carView];
            }
            } else if(indexPath == trainCellNeeded){ 
            UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:TrainCellIdentifier];
                    if (cell == nil) {
                      cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:TrainCellIdentifier] autorelease]; 
                [cell addSubview:trainView];
                 }
            }
          return cell; 
    }
    
    1. 或者为您要添加的子视图指定一个特殊标记,当该单元格再次返回以供重用时,您可以通过其标记访问该特定子视图。

答案 2 :(得分:1)

我会将两个自定义子视图添加到笔尖并将它们连接到插座。根据内容,当您配置单元格的内容时,我会隐藏其中一个。

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *CellIdentifier = @"CellIdentifier";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (!cell) {
        cell = /* load from nib */
    }
    if (/*indexPath conditionForTrainCell*/) {
       cell.trainSubview.hidden = NO;
        cell.carSubview.hidden = YES;
        // configure train cell
    }
    else {
       cell.trainSubview.hidden = YES;
        cell.carSubview.hidden = NO;
        // configure car cell
    }
    return cell;
}

答案 3 :(得分:0)

最简单的方法是创建一个自定义的UITableViewCell子类并为其创建一个xib。将xib中的根视图设置为uitableviewCell,并将其类设置为UITableViewCell子类。将文件所有者类设置为TableViewController子类,并添加所需的所有子视图。那么你可以简单地说:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString * CellIdentifier = @"cellIdentifier";

    TableViewMessageCell * cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    if (cell == nil)
    {
        cell = [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([TableViewMessageCell class])
                                              owner:self
                                            options:nil] lastObject];
    }

    Message * message = [self.fetchedResultsController objectAtIndexPath:indexPath];
    cell.message      = message;

    return cell;
}