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

发布于 2024-11-04 20:58:29 字数 1194 浏览 3 评论 0原文

我正在尝试使用 ArrayControllerBindings 设置带有自定义单元格的 NSTableView。为了实现此目的,我向自定义单元格添加了一个子视图。数据连接似乎有点工作。不过,似乎存在我无法修复的重画问题。当我加载应用程序时,仅呈现一些单元格。当我滚动浏览行或选择一行时,渲染会发生变化。

我在 github 上创建了一个 示例项目来说明问题所在。

Screenshot

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

// 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 没有重绘。我可以以某种方式强迫它吗?

I am trying to setup a NSTableView with a custom cell using an ArrayController and Bindings. To accomplish this I added a subview to the custom cell. The data connection seems to work somewhat. Though, there seems to be a redraw problem which I cannot fix. When I load the application only some of the cells are rendered. When I scroll through the rows or select one the rendering changes.

I created an example project on github to illustrate what the problem is.

Screenshot

The actual source code for the cell rendering can be found 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];
}

It seems as if the parent controlView does not redraw. Can I force it somehow?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

荒人说梦 2024-11-11 20:58:29

这几乎肯定不是执行此操作的最佳实践方法,稍后我将解释原因:但是,它似乎确实有效。将单元格类的 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... 方法中更新一个视图时,您需要查找正确的视图并更新一下。您还需要在代码中分配所有这些视图(或者至少将视图移动到一个单独的笔尖,您可以加载多个副本),因为实际上您的笔​​尖中只有一个,并且复制视图是一种痛苦。

我写的代码是一个拼凑的代码,因为它的效率很低。我所做的是每次需要绘制视图时,我将视图绘制到屏幕外图像缓冲区中,然后将缓冲区绘制到表视图中的正确位置。这样做,我避免了只有一个视图的问题,因为代码只是在需要时获取并绘制其内容的新副本。
在此处输入图像描述

This is almost certainly not a best practice way of doing this, and I'll explain why afterwards: however, it does seem to work. Replace your cell class's drawInteriorWithFrame:inView: method with the following:

- (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];
}

The problem is that only one NSCell is created for the entire table. That's how cells are meant to work: the table view creates a cell, and calls setObject… followed by drawInterior… over and over again to get the cell to draw the whole table. That's great from an efficiency perspective (the NSCell class was designed back when 25mhz was a fast computer, so it aimed to minimise the number of object allocations), but causes problems here.

In your code, you populate a view with values, and set its frame, adding it as a subview of the table view if needed. However, since you've only got one instance of NSCell, there can only be one view: you took the single view that you had and merely moved it down the rows of the table.

To do this properly, you'd need some data structure to track all the views you added as subviews of your NSTableView, and when the cell is updating one in the drawInterior… method you'd need to look up which the correct one was and update that. You'd also need to allocate all these views in code (or at least move the view to a separate nib which you could load multiple copies of), because as it is you've only got one in your nib and copying a view is a pain.

The code I wrote is a kludge, since it's really inefficient. What I did was each time the view needs to draw, I drew the view into an off screen image buffer, and then drew the buffer into the correct place in the table view. In doing so, I avoided the problem of only having one view, since the code just takes and draws a new copy of its contents whenever it is needed.
enter image description here

忆梦 2024-11-11 20:58:29

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

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

EDIT: See my other answer for explanation

Have you implemented copyWithZone:? You'll need to ensure you either copy or recreate your view in that method, otherwise different cells will end up sharing a view (because NSTableView copies its cells).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文