基于视图的NSTableView,使行高取决于内容

时间:2014-01-18 11:03:22

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

我需要帮助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)      |
     |           |                                                   |
     +-----------+---------------------------------------------------+

2 个答案:

答案 0 :(得分:1)

首先,您不应该设置虚拟视图的边界。如果您没有使用自动布局,则应设置框架

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

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

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

这应该为初始内容获得正确的高度。

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

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

答案 1 :(得分:0)

当samplingView的宽度发生变化时,您需要调用tableView的noteHeightOfRowsWithIndexesChanged:方法。您的"参考文献1中提到了这种方法。正如它在方法文档中所述:

  

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

     

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

表格视图的行高度已缓存。从概念上讲,当您想要更改行高时,必须将高度标记为脏,以获得为您指定的行重新调用的委托方法。没有像使用KVO的cocoa绑定那样监视影响rowHeight的所有keyPath的变化,至少在默认情况下是这样。