我需要帮助NSTableView和动态行高
我所拥有的:
绑定到阵列控制器的基于单列视图的NSTableVIew。每个NSTableCellView包含三个子视图:NSImageView,NSTextField(单行)和NSTextField(多行)。基本上,这是一个聊天界面,因此您将拥有一个消息,发件人和头像的列表。
我想要实现的目标:
当文本长于行的最小高度时,行会展开以适合内容。就像iMessage一样,气泡扩展到适合消息。
......这似乎是一件非常自然的事情,但在我在网上找到的所有相关解决方案中,(Ref 1,Ref 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) |
| | |
+-----------+---------------------------------------------------+
答案 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的变化,至少在默认情况下是这样。