18

我以前曾经问过这个问题,但我对解决方案不太满意。

自动调整 NSTableView 的大小

我想NSTableView在 aNSPopover或 a 中显示 a NSWindow
现在,窗口的大小应该随着表格视图而调整。

就像 Xcode 一样:


在此处输入图像描述 在此处输入图像描述


这使用自动布局相当简单,您只需将内部视图固定到超级视图即可。

我的问题是,我无法确定表格视图的最佳高度。下面的代码枚举了所有可用的行,但它没有返回正确的值,因为表视图还有其他元素,如分隔符和表头。

- (CGFloat)heightOfAllRows:(NSTableView *)tableView {
    CGFloat __block height;
    [tableView enumerateAvailableRowViewsUsingBlock:^(NSTableRowView *rowView, NSInteger row) {
        // tried it with this one
        height += rowView.frame.size.height;

        // and this one
        // height += [self tableView:nil heightOfRow:row];
    }];

    return height;
}

1. 问题

我怎样才能解决这个问题?如何正确计算表格视图所需的高度。

2. 问题

我应该在哪里运行这段代码?
我不想在控制器中实现它,因为它绝对是表格视图应该自己处理的东西。
而且我什至没有找到任何有用的委托方法。

所以我想最好的办法是你可以继承NSTableView.
所以我的问题2,在哪里实施?


动机

绝对值得赏金

4

5 回答 5

11

此答案适用于 Swift 4,针对 macOS 10.10 及更高版本:

1.回答

您可以使用表格视图fittingSize来计算弹出框的大小。

tableView.needsLayout = true
tableView.layoutSubtreeIfNeeded()

let height = tableView.fittingSize.height

2. 回答

我理解您希望将该代码移出视图控制器的愿望,但由于表视图本身对项目数量(仅通过委托)或模型更改一无所知,因此我会将其放在视图控制器中。从 macOS 10.10 开始,您可以preferredContentSizeNSViewController内部使用弹出框来设置大小。

func updatePreferredContentSize() {
    tableView.needsLayout = true
    tableView.layoutSubtreeIfNeeded()

    let height = tableView.fittingSize.height
    let width: CGFloat = 320

    preferredContentSize = CGSize(width: width, height: height)
}

在我的示例中,我使用的是固定宽度,但您也可以使用计算得出的宽度(尚未测试)。

每当您的数据源更改和/或即将显示弹出框时,您都希望调用 update 方法。

我希望这能解决你的问题!

于 2017-05-21T04:55:59.983 回答
5

您可以查询最后一行的框架以获取表格视图的高度:

- (CGFloat)heightOfTableView:(NSTableView *)tableView
{
    NSInteger rows = [self numberOfRowsInTableView:tableView];
    if ( rows == 0 ) {
        return 0;
    } else {
        return NSMaxY( [tableView rectOfRow:rows - 1] );
    }
}

这假设一个没有边框的封闭滚动视图!

您可以查询tableView.enclosingScrollView.borderType来检查滚动视图是否有边框。如果是,则需要将边框宽度添加到结果中(两次;底部和顶部)。不幸的是,我不知道如何获得边框宽度。

查询的优点rectOfRow:是它与[tableView reloadData];. reloadData以我的经验,当你第一次查询表格视图的框架时(你会得到以前的高度),查询表格视图的框架并不可靠。

于 2016-02-09T15:24:16.140 回答
3

Xcode 中的 Interface Builder 会自动将 NSTableView 放入 NSScrollView 中。NSScrollView 是标题实际所在的位置。在窗口中创建一个 NSScrollView 作为您的基本视图,并将 NSTableView 添加到其中:

NSScrollView * scrollView = [[NSScrollView alloc]init];
[scrollView setHasVerticalScroller:YES];
[scrollView setHasHorizontalScroller:YES];
[scrollView setAutohidesScrollers:YES];
[scrollView setBorderType:NSBezelBorder];
[scrollView setTranslatesAutoresizingMaskIntoConstraints:NO];

NSTableView * table = [[NSTableView alloc] init];
[table setDataSource:self];
[table setColumnAutoresizingStyle:NSTableViewUniformColumnAutoresizingStyle];
[scrollView setDocumentView:table];

//SetWindow's main view to scrollView

现在可以查询scrollView的contentView来查找NSScrollView的大小

NSRect rectOfFullTable = [[scrollView contentView] documentRect];

因为 NSTableView 位于 NSScrollView 内部,所以 NSTableView 将有一个 headerView,您可以使用它来查找标题的大小。

当表大小发生变化(标题+行)时,您可以继承 NSScrollView 来更新它的超级视图,方法是覆盖 reflectScrolledClipView: 方法

于 2013-01-08T18:18:32.383 回答
2

我不确定我的解决方案是否比您的解决方案更好,但我想我还是会提供它。我将它与打印视图一起使用。我没有使用自动布局。它仅适用于绑定——需要调整才能使用数据源。

你会看到有一个可怕的 hack 让它工作:我只是在我仔细计算的值上加上 0.5。

这会考虑间距,但不考虑我不显示的标题。如果要显示标题,则可以在-tableView:heightOfRow:方法中添加它。

NSTableView子类或类别中:

- (void) sizeHeightToFit {
    CGFloat height = 0.f;
    if ([self.delegate respondsToSelector:@selector(tableView:heightOfRow:)]) {
        for (NSInteger i = 0; i < self.numberOfRows; ++i)
            height = height +
                     [self.delegate tableView:self heightOfRow:i] +
                     self.intercellSpacing.height;
    } else {
        height = (self.rowHeight + self.intercellSpacing.height) *
                 self.numberOfRows;
    }

    NSSize frameSize = self.frame.size;
    frameSize.height = height;
    [self setFrameSize:frameSize];
}

在表视图委托中:

// Invoke bindings to get the cell contents
// FIXME If no bindings, use the datasource
- (NSString *) stringValueForRow:(NSInteger) row column:(NSTableColumn *) column {
    NSDictionary *bindingInfo = [column infoForBinding:NSValueBinding];

    id object = [bindingInfo objectForKey:NSObservedObjectKey];
    NSString *keyPath = [bindingInfo objectForKey:NSObservedKeyPathKey];

    id value = [[object valueForKeyPath:keyPath] objectAtIndex:row];

    if ([value isKindOfClass:[NSString class]])
        return value;
    else
        return @"";
}

- (CGFloat) tableView:(NSTableView *) tableView heightOfRow:(NSInteger) row {

    CGFloat result = tableView.rowHeight;

    for (NSTableColumn *column in tableView.tableColumns) {
        NSTextFieldCell *dataCell = column.dataCell;
        if (![dataCell isKindOfClass:[NSTextFieldCell class]]) continue;

        // Borrow the prototype cell, and set its text
        [dataCell setObjectValue:[self stringValueForRow:row column:column]];

        // Ask it the bounds for a rectangle as wide as the column
        NSRect cellBounds = NSZeroRect;
        cellBounds.size.width = [column width]; cellBounds.size.height = FLT_MAX;
        NSSize cellSize = [dataCell cellSizeForBounds:cellBounds];

        // This is a HACK to make this work.
        // Otherwise the rows are inexplicably too short.
        cellSize.height = cellSize.height + 0.5;

        if (cellSize.height > result)
            result = cellSize.height;
    }

    return result;
}
于 2013-01-17T15:14:45.107 回答
-2

只需将-[NSTableView frame] NSTableView 嵌入到 NSScrollView 中,但具有完整尺寸。

于 2013-01-16T14:52:15.157 回答