如何使用子视图使用自定义单元格设置NSTableView

时间:2011-05-04 13:36:55

标签: objective-c nstableview nscell nstextfieldcell custom-cell

我正在尝试使用 ArrayController Bindings 设置自定义单元格的 NSTableView 。为此,我在自定义单元格中添加了一个子视图。数据连接似乎有点奏效。虽然,似乎有一个我无法解决的重绘问题。当我加载应用程序时,只渲染一些单元格。当我滚动行或选择一个渲染更改时。

我创建了一个example project on github来说明问题所在。

Screenshot

可以找到单元格渲染的实际源代码here

// CustomCell.m
- (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView*)controlView {

  if (![m_view superview]) {
    [controlView addSubview:m_view];
  }

  // The array controller only gets wrapped data items pack by the NSObjectTransformer.
  // Therefore, objectValue returns a NSObjectWrapper.
  // Unpack the wrapper to retreive the data item.
  DataItem* dataItem = [(NSObjectWrapper*)[self objectValue] original];
  [[m_view name] setStringValue:dataItem.name];
  [[m_view occupation] setStringValue:dataItem.occupation];
  [m_view setFrame:cellFrame];
}

似乎父controlView没有重绘。我可以以某种方式强迫它吗?

2 个答案:

答案 0 :(得分:3)

这几乎肯定不是这样做的最佳实践方式,我将在后面解释原因:但是,它似乎确实有效。用以下内容替换您的单元格类的drawInteriorWithFrame:inView:方法:

- (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView*)controlView {
    DataItem* dataItem = [(NSObjectWrapper*)[self objectValue] original];
    [[m_view name] setStringValue:dataItem.name];
    [[m_view occupation] setStringValue:dataItem.occupation];
    [m_view setFrame:cellFrame];

    NSData *d = [m_view dataWithPDFInsideRect:[m_view bounds]];
    NSImage *i = [[NSImage alloc] initWithData:d];
    [i setFlipped:YES];

    [i drawInRect:cellFrame fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0];
}

问题是只为整个表创建了一个NSCell。这就是单元格的工作方式:表格视图创建一个单元格,并调用setObject ...然后反复绘制drawInterior ...以使单元格绘制整个表格。从效率的角度来看这很好(NSCell类是在25mhz是快速计算机时设计的,所以它的目的是最小化对象分配的数量),但这会引起问题。

在代码中,使用值填充视图,并设置其框架,并根据需要将其添加为表视图的子视图。但是,由于您只有一个NSCell实例,因此只能有一个视图:您使用了单个视图,只是将其移动到表格的行中。

要正确执行此操作,您需要一些数据结构来跟踪您添加为NSTableView子视图的所有视图,当单元格在drawInterior ...方法中更新时,您需要查找哪个正确一个是并更新。您还需要在代码中分配所有这些视图(或者至少将视图移动到可以加载多个副本的单独nib),因为因为它只是你的笔尖中有一个并且复制视图是痛苦。

我写的代码是一个kludge,因为它真的很低效。我所做的是每次视图需要绘制时,我将视图绘制到屏幕外图像缓冲区,然后将缓冲区绘制到表视图中的正确位置。在这样做的过程中,我避免了只有一个视图的问题,因为代码只需要它并在需要时绘制其内容的新副本。 enter image description here

答案 1 :(得分:0)

编辑:请参阅我的其他答案以获得解释

您是否已实施copyWithZone:?您需要确保在该方法中复制或重新创建视图,否则不同的单元格最终将共享一个视图(因为NSTableView会复制其单元格)。