【问题标题】:View-based NSTableView, make row height dependent on content基于视图的NSTableView,让行高依赖于内容
【发布时间】:2014-02-07 19:19:29
【问题描述】:

我需要一些关于 NSTableView 和动态行高的帮助

我有什么:

绑定到数组控制器的基于单列视图的 NSTableVIew。每个 NSTableCellView 包含三个子视图:NSImageView、NSTextField(单行)和 NSTextField(多行)。基本上,这是一个聊天界面,所以你会有一个消息、发件人和头像的列表。

我想要达到的目标:

当文本长于行的最小高度时,行会扩展以适应内容。就像 iMessage 一样,气泡会扩大以适应消息。

...这似乎是一件很自然的事情,但是在我在网上找到的所有相关解决方案中,(Ref 1Ref 2),没有一个对我有用。

参考 2 看起来写得非常棒,但这些都不适用于我的应用程序,因为示例项目使用第三方自动布局代码,并且整个东西都是为 iOS 设计的。参考文献 1 给出了一个非常有前途的解决方案,它是用英语写的。我尝试使用解决方案中描述的“虚拟视图”进行设置,但甚至无法正确更改和测量高度。

这是我的tableView:heightOfRow: 代码,_samplingView 是虚拟视图,它有工作限制,与tableView 中的相同。

- (CGFloat)tableView:(NSTableView *)tableView heightOfRow:(NSInteger)row
{
    NSTextField *textField;
    NSTextFieldCell *messageCell;
    for (NSView *subview in [_samplingView subviews]) {
        if ([[subview identifier] isEqualToString:@"message"]) {
            textField = (NSTextField*)subview;
            messageCell = ((NSTextField*)subview).cell;
        }
    }
    Message *message = [[_messagesArrayController arrangedObjects] objectAtIndex:row];
    _samplingView.objectValue = message;

    CGFloat width = [[[tableView tableColumns] objectAtIndex:1] width];
    [_samplingView setBounds:NSMakeRect(0, 0, width, CGFLOAT_MAX)];
    [_samplingView display];

    CGFloat optimalHeight = 10 + [messageCell cellSize].height; //messageCell's size stays the same when I change samplingView to try to measure the height
    return optimalHeight;

}

结果:所有行高都保持不变,不知何故,当我更改_samplingView 的宽度时,它不会重新调整messageCell 的大小。我认为自动布局会处理这种压缩/扩展,并允许我测量高度。确实,我很困惑。

编辑:供参考,这就是我的观点

     +-----------+---------------------------------------------------+
     |           |  NSTextField                                      |
     |NSImageView|  sender                                           |
     |  avatar   +---------------------------------------------------+
     |           |                                                   |
     |           |  NSTextField (multiline)                          |
     +-----------|  message                                          |
     |           |                                                   |
     |           |  (high compression/hugging priority)              |
     |           |  (this view should decide the height of row)      |
     |           |                                                   |
     +-----------+---------------------------------------------------+

【问题讨论】:

  • 你找到解决办法了吗?
  • @Jai,我试了一下。

标签: objective-c cocoa interface-builder xcode5 nstableview


【解决方案1】:

首先,您不应该设置虚拟视图的边界。如果你没有使用自动布局,你可以设置 frame

鉴于您使用的是自动布局,您也不应该这样做。您应该暂时向虚拟视图添加一个约束来限制其宽度。

那么,不要打电话给-display。调用-layoutSubtreeIfNeeded,然后查询虚拟视图的fittingSize。行高应该是距fittingSize 的高度。我不确定您为什么要尝试将其基于messageCell 的高度加上一些任意幻数。不必访问文本字段或其单元格。

你说你有单元格视图正常工作的约束。但是,您需要确保您有约束使单元格视图的高度足够大以容纳子视图。这可能是一个问题,因为表格视图将控制真实(非虚拟)单元格视图的高度,并且如果表格视图暂时使其太短而无法以所需的间距容纳子视图,您将获得无法满足的约束错误。因此,解决方案是将单元格视图底部(或高度)的垂直约束的优先级降低到 1000 以下(必需)。它可能是 250(低)。它只需要大于 50,即NSLayoutPriorityFittingSizeCompression。这是fittingSize 临时用来尝试使视图尽可能小的约束的优先级。

这应该使初始内容的高度正确。

正如您的 Ref 1(和 stevesilva)所指出的,您需要以某种方式观察可变高度多行文本字段内容的变化。当它发生变化时,您需要通过调用-noteHeightOfRowsWithIndexesChanged: 来告诉表格视图行高(可能已经)发生了变化。

最后,您应该努力使-tableView:heightOfRow: 尽可能高效。因此,我建议您在第一次请求任何行高时计算所有行高并缓存它们。 -tableView:heightOfRow: 应该只从缓存中返回一个值。因此,当您观察到消息已更改时,您应该计算该行的新高度并将其与缓存的高度进行比较。当且仅当高度确实发生了变化——并非所有对内容的更改都会改变高度——将新值存储在缓存中并告诉表格高度已经改变。

【讨论】:

  • 哦,是的,当你想要框架时使用边界......那是杀手。可能是整个问题。
【解决方案2】:

sampleView的宽度变化时需要调用tableView的noteHeightOfRowsWithIndexesChanged:方法。您的“参考文献 1”中提到了这种方法。正如方法文档中所说:

如果委托实现 tableView:heightOfRow:此方法会立即使用委托提供的行高重新平铺表格视图。

对于基于 NSView 的表格,此方法将设置动画。要关闭动画,请创建一个 NSAnimationContext 分组并将持续时间设置为 0。然后调用此方法并结束分组。

表格视图缓存了行高。从概念上讲,当您想要更改行高时,您必须将高度标记为脏,以便为您指定的行重新调用您已实现的委托方法。没有什么比 cocoa-bindings 使用 KVO 来监视影响 rowHeight 的所有 keyPath 的变化了,至少默认情况下不会。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多