从笔尖加载可重用的UITableViewCell


89

我能够设计自定义UITableViewCells并使用http://forums.macrumors.com/showthread.php?t=545061上的线程中介绍的技术很好地加载它们。但是,使用该方法不再允许您使用redirectIdentifier初始化单元格,这意味着您必须在每次调用时为每个单元格创建全新的实例。有没有人找到一种仍然可以缓存特定单元格类型以供重用,但是仍然能够在Interface Builder中进行设计的好方法?

Answers:


74

只需使用适当的方法签名来实现一个方法:

- (NSString *) reuseIdentifier {
  return @"myIdentifier";
}

在哪里实现这种方法?
克里希南


5
这是有风险的。如果单元格子类有两个子类,并在单个表视图中同时使用这两个子类,会发生什么情况?如果他们将重用标识符调用发送到super上,您将使错误类型的单元出队...........我认为您需要重写useuseIdentifier方法,但要让它返回被取代的标识符串。
SK9

3
为确保其独特性,您可以执行以下操作:return NSStringFromClass([self class]);
ivanzoid

119

实际上,由于您是在Interface Builder中构建单元的,因此只需在此处设置重用标识符即可:

IB_reuse_identifier

或者,如果您正在运行Xcode 4,请检查“属性”检查器选项卡:

在此处输入图片说明

(编辑:您的XIB由XCode生成后,它包含一个空的UIView,但是我们需要一个UITableViewCell;因此您必须手动删除该UIView并插入一个表格视图单元格。当然,IB不会显示任何UITableViewCell参数UIView。)


如果我将此Nib创建的单元格用于多个单元格,该如何设置标识符?这将导致我们拥有两个具有相同标识符的单元格。
克里希南

4
不要将其视为唯一标识符,而应将其更像是类型名称。
Tim Keating

我没有看到通过Xcode 4.3.3中的内置接口构建器设置标识符的选项。我肯定是将该类设置为UITableViewCell子类。我只是想念它,还是消失了?
泰勒(Tyler)

3
好的,我解决了我的问题。如果从接口构建器中的UIView对象开始(在Xcode 4内),并将其类更改为UITableViewCell,则不会获得特定于单元格的属性,例如重用标识符。为此,您应该以一个空的xib开始并拖入一个表格单元格对象,然后该表格单元格将具有您可以编辑的特定于单元格的属性。
泰勒(Tyler)

1
@Krishnan这么想-当您创建带有标识符X的表视图单元格时,您会说“请给我一个标记为X的池中的单元格。” 如果该池存在,并且那里有一个空闲单元,那么它将提供给您。否则,它将创建池(如有必要),然后对单元进行新闻处理,将其标记为“ X”,然后将其交给您。因此单元可以是唯一的-例如,您可以创建一个只有一个具有特定标识符的单元的池-但该库使用类似于自由列表的策略来避免内存分配/取消分配。
Tim Keating 2012年

66

现在,在iOS 5中,有一个适合的UITableView方法:

- (void)registerNib:(UINib *)nib forCellReuseIdentifier:(NSString *)identifier

10
该主题中的其他答复(包括已接受的答案)均包含过时的建议。
Kaelin Colclasure,2012年

向后兼容吗?我的意思是,如果我使用SDK 5.0开发一个应用程序并且目标最低为4.0,那么该应用程序是否可以在具有iOS 4.0的设备上运行?
Abolfoooud 2012年

1
不,这不像iOS 5.0中的任何新API那样向后兼容。
marzapower

:如何将其整合到您的控制器工作实例mindfiresolutions.com/...
mblackwell8

47

我不记得最初在哪里找到此代码,但是到目前为止,它对我来说一直很好。

- (UITableViewCell *)tableView:(UITableView *)tableView 
         cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    static NSString *CellIdentifier = @"CustomTableCell";
    static NSString *CellNib = @"CustomTableCellView";

    UITableViewCell *cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        NSArray *nib = [[NSBundle mainBundle] loadNibNamed:CellNib owner:self options:nil];
        cell = (UITableViewCell *)[nib objectAtIndex:0];
    }

    // perform additional custom work...

    return cell;
}

接口构建器设置示例...

替代文字


12

看看我对这个问题的回答:

是否可以在Interface Builder中设计NSCell子类?

不仅可以在IB中设计UITableViewCell,而且还很理想,因为否则所有手动布线和多个元素的放置都非常繁琐。只要可以使所有元素不透明,性能就可以。在IB中为UITableViewCell的属性设置了复用ID,然后在尝试出队时在代码中使用匹配的复用ID。

去年在WWDC上的一些演讲者中,我还听说您不应该在IB中创建表格视图单元格,但这是一大堆东西。


2
如果您需要透明度并且想要良好的滚动性能,则不应在IB中创建表格视图单元格。对于某些UI,您需要透明性(例如,通过图形渲染文本)。有时,在较旧的(A4之前的)硬件上实现良好滚动的唯一方法是用代码进行渲染,以避免GPU必须合成多个透明层。
Nick Forge 2010年

2
没错,但是即使这样,您也可以更好地让旧设备上的性能受到影响,因为内置IB的单元更易于维护。您也可以将此技术用作模板单元格,然后从中绘制元素,以避免与自定义绘制方法进行合成。
肯德尔·赫尔姆斯特·盖尔纳


6

这是另一个选择:

NSString * cellId = @"reuseCell";  
//...
NSArray * nibObjects = [[NSBundle mainBundle] loadNibNamed:@"CustomTableCell" owner:nil options:nil];

for (id obj in nibObjects)
{
    if ([obj isKindOfClass:[CustomTableCell class]])
    {
        cell = obj;
        [cell setValue:cellId forKey:@"reuseIdentifier"];
        break;
    }
}

请注意,这是迄今为止发布的唯一解决方案,不需要为您的自定义子类UITableViewCell设置的唯一值reuseIdentifer。我认为这是原始操作人员真正想要的。
charshep 2012年

我希望Apple不会拒绝我使用此应用程序的应用程序...我正在使用它来获取“静态”单元格,因为在我的表格视图中,填充单元格的过程非常缓慢。这样,我只需要执行一次(给每行一个不同的标识符)。谢谢!
RicardPérezdel Campo

非常有用,因为没有UITableViewCell方法可以手动设置!
杰西

2

我以类似的方式创建自定义视图单元格-除了通过IBOutlet连接单元格外。

[nib objectAt...]方法容易改变阵列中项目的位置。

这种UIViewController方法很好-只是尝试了一下,效果很好。

但...

在所有情况下,initWithStyle都不会调用构造函数,因此不会进行默认初始化。

我已经阅读过许多有关使用initWithCoder或的地方awakeFromNib,但没有确凿的证据表明这两种方法都是正确的。

除了在方法中显式调用某些初始化方法外,cellForRowAtIndexPath我还没有找到答案。


awakeFromNib是对从NIB加载的对象做出反应的正确方法。
乔恩·赫斯

2

不久前,我在blog.atebits.com上找到了一篇有关该主题的出色博客文章,此后开始使用Loren Brichter ABTableViewCell类来处理我的所有UITableViewCells。

您最终得到了一个简单的容器UIView来放置所有小部件,并且滚动速度很快。

希望这是有用的。


2

此技术也可以使用,并且不需要在视图控制器中使用时髦的ivar进行内存管理。在这里,定制表视图单元格位于名为“ CustomCell.xib”的xib中。

 static NSData *sLoadedCustomCell = nil;

 cell = [tableView dequeueReusableCellWithIdentifier:@"CustomCell"];
 if (cell == nil) 
 {
   if (sLoadedCustomCell == nil) 
   {        
      // Load the custom table cell xib
      // and extract a reference to the cell object returned
      // and cache it in a static to avoid reloading the nib again.

      for (id loadedObject in [[NSBundle mainBundle] loadNibNamed:@"CustomCell" owner:nil options:nil]) 
      {
        if ([loadedObject isKindOfClass:[UITableViewCell class]]) 
        {
          sLoadedCustomCell = [[NSKeyedArchiver archivedDataWithRootObject: loadedObject] retain];
          break;
        }
    }
    cell = (UITableViewCell *)[NSKeyedUnarchiver unarchiveObjectWithData: sLoadedCustomCell];
  }

1
存档和取消存档完全没有必要。
布莱恩·亨利

1
如果您可以在无法出队的情况下从其笔尖加载该单元,则无需存档/取消存档。但是,如果只想从其笔尖仅一次加载该单元,则需要将其缓存在内存中。我使用NSKeyedArchiving完成了缓存,因为UITableViewCell没有实现NSCopying。
比尔·加里森

1
综上所述,使用UINib加载单元可获得相同的效果:从磁盘加载一次,然后从内存加载。
比尔·加里森

2

路易法对我有用。这是我用来从笔尖创建UITableViewCell的代码:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{   
    UITableViewCell *cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:@"CustomCellId"];

    if (cell == nil) 
    {
        UIViewController *c = [[UIViewController alloc] initWithNibName:@"CustomCell" bundle:nil];
        cell = (PostCell *)c.view;
        [c release];
    }

    return cell;
}

2
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

static NSString *simpleTableIdentifier = @"CustomCell";

CustomCell *cell = (CustomCell *)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil)
{
    NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"CustomCell" owner:self options:nil];
    cell = [nib objectAtIndex:0];

    [cell setSelectionStyle:UITableViewCellSelectionStyleNone];
}         

return cell;
}

1

gustavogb解决方案对我不起作用,我尝试过的是:

ChainesController *c = [[ChainesController alloc] initWithNibName:@"ChainesController" bundle:nil];
[[NSBundle mainBundle] loadNibNamed:@"ChaineArticleCell" owner:c options:nil];
cell = [c.blogTableViewCell retain];
[c release];

它似乎有效。blogTableViewCell是该单元的IBOutlet,而ChainesController是该文件的所有者。


1

来自UITableView文档,涉及dequeueWithReuseIdentifier:“一个字符串,用于标识要重用的单元格对象。默认情况下,可重用单元格的标识符是其类名,但是您可以将其更改为任意值。”

覆盖-reuseIdentifer自己是有风险的。如果单元格子类有两个子类,并在单个表视图中同时使用这两个子类,会发生什么情况?如果他们将重用标识符调用发送到super上,您将使错误类型的单元出队...........我认为您需要重写useuseIdentifier方法,但要让它返回被取代的标识符串。或者,如果未指定,则将其作为字符串返回。


0

对于它的价值,我在一次iPhone技术座谈会上询问了一位iPhone工程师。他的回答是:“是的,可以使用IB创建单元格。但是请不要,请不要。”


1
真奇怪 纽约演讲中至少有两个演讲的演示代码使用了IB中创建的单元。
肖恩·克拉弗

3
不确定我是否相信这一点,因为他们使用IB在Apple的Advanced Table View Cells示例项目中创建了单元格。
iwasrobbed

感谢那。每次这样做,都会遇到问题。这可能就是原因
skorulis

他可能没有足够的知识。
aryaxt 2012年

0

我遵循了本·莫舍(Ben Mosher)链接的苹果公司的指示(谢谢!),但发现苹果公司忽略了重要的一点。他们在IB中设计的对象只是一个UITableViewCell,从中加载的变量也是如此。但是,如果实际上将其设置为UITableViewCell的自定义子类,并编写该子类的代码文件,则可以在代码中编写IBOutlet声明和IBAction方法,并将它们连接到IB中的自定义元素。这样就无需使用视图标签来访问这些元素,您可以创建任何想要的疯狂单元。这是可可触摸天堂。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.