自动调整NSTableView的高度

时间:2013-01-08 17:43:17

标签: objective-c macos cocoa nstableview autolayout

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

Automatically adjust size of NSTableView

我想在NSTableViewNSPopover中显示NSWindow
现在,窗口的大小应该根据表格视图进行调整。

就像Xcode一样:


enter image description here enter image description here


对于自动布局,这非常简单,您可以将内部视图固定到超级视图。

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

- (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,在哪里实现它?


动机

绝对值得赏金

5 个答案:

答案 0 :(得分: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];相同的runloop迭代中工作。根据我的经验,当您首先reloadData(您将获得之前的高度)时,查询表格视图的框架无法可靠地工作。

答案 1 :(得分:4)

  

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

<强> 1。答案

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

tableView.needsLayout = true
tableView.layoutSubtreeIfNeeded()

let height = tableView.fittingSize.height

<强> 2。答案

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

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

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

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

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

您希望在数据源发生变化时和/或当您要显示弹出窗口时调用更新方法。

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

答案 2 :(得分: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,您可以使用它来查找标题的大小。

通过覆盖reflectScrolledClipView:方法

,可以在表大小更改(标题+行)时更新NSScrollView以更新它的超级视图

答案 3 :(得分:2)

我不确定我的解决方案是否比你的解决方案更好,但我认为无论如何我都会提供它。我用它打印视图。我没有使用自动布局。它只适用于绑定 - 需要调整才能使用数据源。

你会看到有一个糟糕的黑客让它发挥作用:我只是在我仔细计算的值上加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;
}

答案 4 :(得分:-2)

得到-[NSTableView frame] NSTableView嵌入在NSScrollView中,但具有完整大小。